Forgejo Webhook Architecture Options

Question: What’s the most resilient way to route Forgejo webhooks to agents?

Current State (Per-Repo Webhooks)

Each repo has 2-3 webhooks pointing directly to the agent gateway.

Problems:

  • Brittle: PATCH clears fields, easy to misconfigure
  • Doesn’t scale: N repos × M webhooks = maintenance burden
  • Manual setup required for each new repo
  • No central visibility into webhook health

Option 1: Site-Wide Webhook → Each Agent

┌─────────────┐     ┌─────────────────┐
│   Forgejo   │────▶│  Agent Gateway  │
│  (system    │     │  192.168.0.252  │
│   webhook)  │     └─────────────────┘
└─────────────┘
      │
      │ (if more agents)
      ▼
┌─────────────────┐
│  Agent 2 GW     │
│  192.168.0.XXX  │
└─────────────────┘

How it works:

  • Admin adds one system webhook per agent
  • Each agent receives ALL events from ALL repos
  • Each agent’s router filters for their @username/assignments

Pros:

  • ✅ Simple, direct, no intermediate layer
  • ✅ Each agent is independent
  • ✅ New repos automatically included
  • ✅ No single point of failure

Cons:

  • ❌ Every agent gets ALL traffic (wasteful)
  • ❌ Requires Forgejo admin access to add/remove agents
  • ❌ N agents = N system webhooks to maintain
  • ❌ No central observability

Best for: 1-2 agents, low event volume


Option 2: Centralized Router (Cybersyn)

┌─────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Forgejo   │────▶│    Cybersyn     │────▶│  Agent Gateway  │
│  (1 system  │     │  Event Router   │     │  192.168.0.252  │
│   webhook)  │     │  192.168.0.250  │     └─────────────────┘
└─────────────┘     └─────────────────┘
                           │
                           │ (routes by @mention, assignment)
                           ▼
                    ┌─────────────────┐
                    │  Agent 2 GW     │
                    │  (future)       │
                    └─────────────────┘

How it works:

  • One system webhook → cybersyn router service
  • Router inspects payload, determines which agent(s) should receive it
  • Routes to appropriate agent gateway(s)
  • Agent registry in cybersyn config/database

Routing logic:

function routeEvent(event, payload) {
  const targets = [];
  
  // @mentions in body/comments
  for (const agent of agents) {
    if (hasMention(payload, agent.username)) {
      targets.push(agent);
    }
  }
  
  // Issue/PR assignment
  if (payload.assignee) {
    const agent = findAgentByUsername(payload.assignee.login);
    if (agent) targets.push(agent);
  }
  
  // Repo ownership/watch list
  for (const agent of agents) {
    if (agent.watchedRepos.includes(payload.repository.full_name)) {
      targets.push(agent);
    }
  }
  
  // Broadcast events (CI failures on commune repos)
  if (isCommunalRepo(payload.repository) && event === 'action_run_failure') {
    targets.push(...agents); // All agents see commune CI issues
  }
  
  return [...new Set(targets)]; // dedupe
}

Pros:

  • ✅ Single webhook to manage (in Forgejo admin)
  • ✅ Smart routing — only relevant agents get events
  • ✅ Add/remove agents without touching Forgejo
  • ✅ Central logging, debugging, observability
  • ✅ Enables cross-agent coordination patterns
  • ✅ Already have cybersyn infrastructure

Cons:

  • ❌ Single point of failure (cybersyn down = no webhooks)
  • ❌ More complex to implement
  • ❌ Extra network hop (latency)
  • ❌ Cybersyn needs agent registry

Mitigation for SPOF:

  • Run cybersyn router as systemd service with auto-restart
  • Health checks + alerting
  • Could add redundant router later if needed

Best for: 2+ agents, growing commune, need observability


Option 3: Hybrid (Site-Wide + Local Filtering)

Same as Option 1, but with smarter local filtering.

How it works:

  • System webhook → each agent directly
  • Each agent’s router is smarter about what to ignore
  • Shared “ignore list” or “agent registry” file synced via git

Pros:

  • ✅ No SPOF (each agent independent)
  • ✅ Simple initial setup
  • ✅ Agents can still coordinate via shared config

Cons:

  • ❌ Still wasteful traffic
  • ❌ No central observability
  • ❌ Coordination requires git sync

Recommendation: Centralized Router

For the commune’s current and near-future needs, Option 2 (Centralized Router on Cybersyn) provides:

  1. Already have cybersyn — natural home for routing logic
  2. One webhook to rule them all — no more per-repo PATCH disasters
  3. Smart routing — only relevant events reach each agent
  4. Observability — central place to debug webhook issues
  5. Enables governance — cybersyn sees all events, can implement consensus/voting
  6. Future-proof — adding agents is a config change, not Forgejo admin

Implementation Plan

  1. Create router service in cybersyn

    • HTTP server listening for webhooks
    • Agent registry (config file or SQLite)
    • Routing logic based on mentions/assignments/repos
  2. Set up system webhook in Forgejo

    • Admin → System Webhooks → Add
    • URL: http://192.168.0.250:3200/webhook/forgejo
    • Events: All (or specific subset)
    • Auth: Bearer token
  3. Migrate existing repos

    • Remove per-repo webhooks (optional, can coexist)
    • Verify events flow through new router
  4. Agent registration

    # cybersyn/config/agents.yaml
    agents:
      - username: agent
        gateway: http://192.168.0.252:18789
        watchedRepos:
          - agent/*
          - commune/*
          - health/*

SPOF Mitigation

  • Systemd service with Restart=always
  • Healthcheck endpoint /health
  • Alert on failures (notify Discord)
  • Eventually: redundant router or queue-based delivery

Event Volume Estimate

Current repos: ~15-20 Events per day: ~50-100 (pushes, CI, occasional issues/PRs) Agents: 1 (soon 2-3?)

This is LOW volume. Even broadcasting to all agents wouldn’t be a problem. Centralized router is still better for observability and maintainability.


Research compiled 2026-02-08 for agent infrastructure planning.