GoClaw keeps lightweight process-local status for the gateway, the webhook listener, the ingress layer, ingress access logs and webhook audit logs in internal/runtimeinfo/runtimeinfo.go. HTTP handlers expose these snapshots to the Web UI and CLI status checks.
| Area | Stored fields | Max retained |
|---|---|---|
| Gateway | bind mode, listen address, rate-limit settings, request counters | Current counters |
| Webhook listener | where /goclaw/hook/ is served: listen address, port, bind mode, whether it is a separate port from the Web UI |
Current values |
| Ingress | enabled flag, provider, source, public URL, upstream, allowlist, counters, last request time | Current counters |
| Ingress access log | timestamp, client IP, method, path, status code, duration, host, source | 200 entries |
| Webhook audit log | channel, event type, author, peer, preview, status | 500 entries |
The state is protected by a package-level mutex. SnapshotState() returns a copy of slices so callers do not mutate shared state.
| Endpoint | Handler | Data |
|---|---|---|
GET /api/stats |
HandleStats |
Gateway status plus BuildRuntimeStats() |
GET /api/gateway?limit=30 |
HandleGateway |
BuildRuntimeStats() plus access_logs: the recent ingress access logs, newest first (limit default 30, at most 200) |
GET /api/audit?channel=&event=&user=&limit= |
HandleAuditLogs |
Filtered webhook audit logs |
/api/stats includes these runtime sections:
{
"gateway_runtime": {
"bind_mode": "loopback",
"listen_addr": "127.0.0.1:8080",
"rate_limit_enabled": true,
"rate_limit_per_min": 60,
"requests_total": 120,
"rate_limited_total": 2
},
"public_ingress": {
"enabled": true,
"provider": "ngrok",
"source": "gateway",
"public_url": "https://example.ngrok.app",
"configured_url": "https://example.ngrok.app",
"upstream": "http://127.0.0.1:8080",
"allow_preset": "",
"allowlist": [],
"forwarded_total": 40,
"error_total": 1,
"denied_total": 0,
"last_request_at": "2026-06-20T08:45:00Z"
},
"webhook_listener": {
"listen_addr": "127.0.0.1:8080",
"port": "8080",
"bind_mode": "loopback",
"separate_port": false,
"hook_path": "/goclaw/hook/",
"webui_port": "8080"
},
"webhooks": {
"msteams": { "enabled": false, "listen_addr": ":8080", "require_mention": false }
},
"webhook_secret_slug": "",
"facebook_enabled": false
}
upstream is where a reverse proxy or ngrok must forward to: the webhook
listener on http://127.0.0.1:<port>, not the public URL. configured_url
is gateway.ingress.url as written in the config. webhook_listener says
where the hook routes are actually served; with gateway.ingress.port set
they move to their own listener (separate_port: true) and the Web UI port
answers them with 404.
gateway.AuthMiddleware records gateway requests.gateway.RateLimitMiddleware records rate-limited requests.cmd/gateway/main.go records the gateway settings (SetGateway) and the webhook listener (SetWebhookListener) at start-up.gateway.Ingress records ingress state, allowlist entries and the denied counter.gateway.AccessLogMiddleware records the access log and the forwarded/error counters, whether or not an ingress is configured: every request on the ingress listener, and on the Web UI listener only requests to webhook paths.runtimeinfo.RecordWebhookAudit.internal/runtimeinfo/runtimeinfo.gointernal/gateway/handler_api.gointernal/gateway/ingress.gointernal/gateway/access_log.gointernal/gateway/middleware.gocmd/gateway/main.gocmd/gateway/startup.go