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:
- Already have cybersyn — natural home for routing logic
- One webhook to rule them all — no more per-repo PATCH disasters
- Smart routing — only relevant events reach each agent
- Observability — central place to debug webhook issues
- Enables governance — cybersyn sees all events, can implement consensus/voting
- Future-proof — adding agents is a config change, not Forgejo admin
Implementation Plan
-
Create router service in cybersyn
- HTTP server listening for webhooks
- Agent registry (config file or SQLite)
- Routing logic based on mentions/assignments/repos
-
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
-
Migrate existing repos
- Remove per-repo webhooks (optional, can coexist)
- Verify events flow through new router
-
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.