Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Access rules

rules:
  allow:
    - enabled: true
      order: 110
      operation: [FETCH, PUSH]
      providers: [github/github.com]
      slugs:
        - /myorg/repo-one
        - /myorg/repo-two

  deny:
    - enabled: true
      order: 100 # deny rules with lower order numbers take precedence
      operation: [PUSH]
      slugs:
        - /myorg/archived-repo

Rules are evaluated in order number order (lower = earlier). Deny rules override allow rules at the same order number. The proxy is default-deny: if no allow rule matches, the request is rejected.

operation scopes a rule to PUSH, FETCH, or both. A repo can be open for fetch but restricted for push.

Disabling fetch serving entirely

Access rules gate which upstreams are reachable for FETCH. A separate, coarser switch controls whether server mode serves clone/fetch from its local mirror at all:

server:
  serve-fetch: false # global default; push-only gateway, no local mirror served

providers:
  github:
    serve-fetch: true # optional per-provider override of the global default

Serving fetches is the default and the right one for most deployments — a developer whose remote is the fogwall URL expects git pull to work against it, and taking that away breaks the single-remote workflow. Turn it off when:

  • fogwall is a push-validation gateway that is not meant to be a read path for anything;
  • the mirror holds repositories you would rather not serve from fogwall’s disk at all, regardless of who asks;
  • you want the reachable surface as small as the use case requires.

When disabled, the git-upload-pack capability is simply not mounted (HTTP) and is refused on the SSH transport; a fetch is refused with a 403 and a message git prints — remote: fetches are not served through this gateway — rather than a 404 that reads as a missing repository. Push (receive-pack) is unaffected, and the switch applies to both server mode transports so neither can serve a fetch the other refuses.

This is deliberately not a per-user read-permission model: for a public upstream there is no credential to authorize, and for a private one the fetch already carries the caller’s own upstream credentials, which answers the question authoritatively. Use access rules to gate which repos are reachable, and serve-fetch to decide whether fogwall serves fetches at all. Transparent proxy mode forwards to upstream rather than serving a local mirror, so it is unaffected by this setting.

Fetch activity

fogwall counts its decisions on clones and fetches rather than recording each one. Every decision increments a counter in memory, keyed by the hour, repository, transport (HTTP or SSH), proxy mode, result, refusal reason, and the access rule that matched. Counts are written to the fetch_activity table every server.fetch-activity.flush-interval-seconds and kept for retention-days; see Server settings. A clone never waits on the database.

The counts drive the fetch and blocked numbers on the Repos page, where each repository’s numbers open its hourly rows. They are also served at GET /api/fetches, filterable by result, transport, provider, owner, repository, and search.

A refusal carries one of these reasons:

ReasonRefused because
NOT_IN_ALLOW_LISTNo allow rule matched the repository.
DENY_RULEA deny rule matched; the row names the rule.
FETCH_DISABLEDserve-fetch is off for the provider.
CREDENTIAL_REFUSEDA fogwall credential was invalid, expired, revoked, or not accepted.
LINKED_TOKEN_UNUSABLEA fogwall credential was valid, but its linked SCM account was not.
SSH_AGENT_MISSINGAn SSH client connected without agent forwarding.

No caller identity is kept: fetch access never depends on one. Up to one flush interval of counts is lost if fogwall stops abruptly or the database refuses a write. Past max-keys distinct combinations between writes, further fetches are counted in a row per provider and outcome that names no repository, and fogwall logs a warning. An allowed fetch logs at DEBUG; a refused one logs at INFO. The fogwall.fetch.decisions metric carries the same decisions without the repository; see Observability.

Before 1.5.0, fogwall wrote one fetch_records row per request. That table (or MongoDB collection) is no longer written or read, and is left in place for you to drop.

Dry-run testing rules and permissions

Available since v1.3.0.

Before rolling out a new access rule or permission grant, verify the outcome against the live configuration without waiting for a real push:

POST /api/repos/rules/test
{ "provider": "github/github.com", "owner": "myorg", "name": "myrepo", "operation": "PUSH" }
→ { "decision": "ALLOW", "matchedRuleId": 110, "steps": [...] }

POST /api/users/{username}/permissions/test
{ "provider": "github/github.com", "path": "/myorg/myrepo", "grant": "PUSH" }
→ { "allowed": true, "source": "GROUP", "groupName": "platform-team" }

Both endpoints are read-only evaluations against whatever rules, permissions, and groups are currently loaded — no push is created. source on the permission check distinguishes a direct per-user grant (DIRECT) from one inherited via a permission group (GROUP). These endpoints are dashboard-only (fogwall-dashboard); the standalone server has no REST API.