agent-core-inbound
Deny-by-default inbound notifications router for agent-core beings.
External signals (GitHub webhooks, Gmail messages, calendar events)
flow through per-source connectors that classify each event as
Allow{tier, reason} or Deny. The router de-dupes, rate-limits,
delivers via the agent-core bus, and writes an audit log.
See docs/superpowers/specs/2026-06-20-inbound-notifications-design.md
in the agent_core repo for the full design.
Bringing v1.a online (operator runbook)
1. Generate the GitHub webhook secret
Pick any high-entropy string; e.g.:
python -c "import secrets; print(secrets.token_urlsafe(32))"
Set it as an env var in the daemon's environment (e.g., your ~/.agent-core/.env or systemd unit):
FOREMAN_GITHUB_WEBHOOK_SECRET=<paste-here>
2. Write your allowance file
~/.<being>/.config/inbound/github-allowance.toml:
# Schema-flexible rule shape (v2). See spec
# docs/superpowers/specs/2026-06-21-inbound-v2-schema-flexible-events-design.md
# for the full grammar.
[[allow]]
rule_id = "pr_review_requested_any_project"
event = "pull_request_review_requested"
match = { "requested_reviewer.login" = "<your-github-login>" }
tier = "red"
reason = "PR review requested on me"
[[allow]]
rule_id = "needs_help_foreman"
event = "issues_labeled"
repo = "<org>/<repo>"
match = { "label.name" = "foreman:needs-help" }
tier = "red"
reason = "Foreman escalation — needs operator unstick"
The reviewer/label_name shortcuts from v1.a still work — they translate to match entries automatically. body_contains was removed in v2 (raises ValueError on load); use exact-equality match instead, or wait for v2.1's match_contains operator.
Body projection. The GitHub connector trims the Notification envelope body to a small per-event-type field set (event type, action, repo, key identifiers). This keeps inline bus payloads under 1 KB and avoids bloating tool results with GitHub metadata your being doesn't need. The full raw webhook payload is always recoverable from GitHub's webhook delivery history via gh api repos/<repo>/hooks/<id>/deliveries/<delivery_id>.
The router watches the file's mtime and reloads on every webhook delivery — edit the TOML and the next event picks up the new rules without restarting the daemon.
3. Register the endpoint in agent_core.yaml
The inbound-notifications endpoint registers via the agent_core pluggy hook (see agent_core_inbound/plugin.py — the inbound.github type is registered automatically once the package is installed).
Add this entry to your agent_core.yaml's endpoints: list:
endpoints:
- type: inbound.github
name: inbound
params:
target_being: <your-being>
listen_host: 127.0.0.1
listen_port: 8765
webhook_secret_env: FOREMAN_GITHUB_WEBHOOK_SECRET
github_allowance_path: ~/.<being>/.config/inbound/github-allowance.toml
audit_log_path: ~/.<being>/state/inbound-audit.jsonl
rate_limit_per_minute: 30
The runner reads each entry's type and looks it up in the pluggy-registered endpoint types map. The name is the bus addressing name (also surfaces in agent-core ps). All params are passed as constructor kwargs to InboundEndpoint.
4. Start Tailscale Funnel
tailscale funnel 8765
Note the issued https://router.<tailnet>.ts.net URL.
5. Configure the GitHub webhook
In your repo settings → Webhooks → Add webhook:
- Payload URL:
https://router.<tailnet>.ts.net/github - Content type:
application/json - Secret: the same value you stored in
FOREMAN_GITHUB_WEBHOOK_SECRET - Which events: "Let me select individual events" — check Workflow runs, Pull requests, Pull request reviews, Issues, Issue comments, Pushes, Releases, Statuses. (Schema-flexible matching means we can add more later via
gh api -X PATCH repos/<repo>/hooks/<id> -f events='[...]'with no daemon change.)
6. Smoke test
On any PR in your configured repo, request a review from @<your-github-login>. Within ~10s:
~/.<being>/state/inbound-audit.jsonlgains anallowline withrule_id=pr_review_requested_any_project.- Your being's bus inbox receives a
Notificationenvelope (urgencyred).
If you instead see a deny line, double-check match = { "requested_reviewer.login" = "<your-github-login>" } in the allowance TOML against the actual reviewer GitHub login.
Troubleshooting
- All POSTs land 401: the env var secret does not match the GitHub webhook secret. Re-paste both ends.
BusBootError: unknown endpoint type 'inbound.github': theagent-core-inboundpackage isn't installed in the daemon's environment, or its entry point isn't being discovered. Runuv sync(or your install path equivalent) and confirmpython -c "import agent_core_inbound.plugin"succeeds.- Webhook delivers but no bus envelope: check the audit log first. If
denylines appear, the allowance rule isn't matching — verify theevent,repo, andmatchdotted-path keys against the actual webhook payload (visible in GitHub's webhook delivery history). - No audit log writes at all: the endpoint isn't seeing the POST. Confirm Tailscale Funnel is active (
tailscale funnel status) and the daemon log showsInboundEndpoint(name=inbound) started on 127.0.0.1:8765. denywithreason="no_matching_rule"on every event: v2 parses every webhook event generically (no silent 204 fallback), so the connector denies anything not matched by an allow rule. If you expected an allow, the rule'seventkey (e.g.pull_request_opened) ormatchdotted-paths don't line up with the payload shape — check the webhook delivery body in GitHub's "Recent Deliveries" UI to confirm the exact field names.- Webhook deliveries land 404 from uvicorn: the Tailscale Funnel command should be
tailscale funnel <port>— do NOT use--set-path=/github. That flag STRIPS the path prefix before forwarding, leaving uvicorn to seePOST /(no route). The default mount at/is correct because the FastAPI route is at/github.
Metadata
Release files for agent-core-inbound 0.9.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_core_inbound-0.9.5.tar.gz | 35.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_core_inbound-0.9.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.8 kB
Release files / agent_core_inbound-0.9.5.tar.gz
| Download URL | agent_core_inbound-0.9.5.tar.gz |
|---|---|
| Size | 35.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07ffb737446699bf6c775262ac98ce0cb9e1d9a5c2c963ed24e7829953ac7d11
|
|
BLAKE2b-256 checksum How to use checksums |
068ccee5a443a118202e156c73022861d68206404a2b8349c2acedf4dff43d90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Release files / agent_core_inbound-0.9.5-py3-none-any.whl
| Download URL | agent_core_inbound-0.9.5-py3-none-any.whl |
|---|---|
| Size | 23.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
db9459721810dd7b3d1954d2b3dd3026bf21c7c92e87d29c88fc431a8883c42b
|
|
BLAKE2b-256 checksum How to use checksums |
7e9923922087e73e7118ec5080f6fe7fc4240e49f50581a32d7b92e19184a88e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|