Skip to content

Server/Dashboard Decoupling: Fix standalone store-and-forward mode #13

@coopernetes

Description

@coopernetes

Problem

jgit-proxy-server is supposed to run standalone (proxy-only, no dashboard), but store-and-forward mode is functionally broken without a dashboard.

GitProxyServletRegistrar.registerGitServlet() hardwires UiApprovalGateway:

var approvalGateway = new UiApprovalGateway(pushStore);

UiApprovalGateway polls PushStore every 5 seconds waiting for a human approval via the REST API — which only lives in jgit-proxy-dashboard. Every push lands in BLOCKED and hangs the git client for 30 minutes before timing out.

Proxy mode (/proxy/) works fine standalone today. Store-and-forward does not.

Root Cause

Three hardcoded concerns:

  1. ApprovalGateway selectionUiApprovalGateway is constructed directly despite ApprovalGateway being an interface with multiple implementations
  2. Store-and-forward hook chain — fixed array of hooks inline; persistenceHook != null branch exists but no other extension points
  3. Proxy filter chainAllowApprovedPushFilter (dashboard concern) is unconditionally registered by the server-level registrar

Plan

Phase 1 — Fix standalone server (high ROI, minimal change)

  • Add AutoApprovalGateway to jgit-proxy-core — auto-approves clean pushes instantly, rejects violations immediately (no polling)
  • Add approval-mode config key (auto | ui); default auto for the server-only entry point
  • Add JettyConfigurationBuilder.buildApprovalGateway(PushStore) to instantiate from config
  • Thread ApprovalGateway through registerGitServlet(...) as an explicit parameter — remove hardwired new UiApprovalGateway(pushStore)
  • GitProxyWithDashboardApplication always constructs and passes UiApprovalGateway regardless of config

Phase 2 — Decouple filter and hook chains

  • Split registerFilters into registerCoreFilters() (always runs) and registerApprovalFilters() (dashboard calls additionally) — mirrors the existing persistenceHook != null conditional
  • GitProxyWithDashboardApplication calls both; standalone server calls only registerCoreFilters()
  • Defer further hook chain abstraction unless a concrete third deployment type emerges

Module Changes

Module Change
jgit-proxy-core Add AutoApprovalGateway
jgit-proxy-server Add approval-mode config; buildApprovalGateway(); ApprovalGateway param on registerGitServlet; split registerFilters
jgit-proxy-dashboard Pass UiApprovalGateway explicitly; call registerApprovalFilters()

Non-Goals

  • Full plugin SPI for hooks/filters
  • Removing PushStore from standalone (useful for audit logging; memory backend needs no infra)
  • Changing the transparent proxy path (works correctly standalone today)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions