main · last commit
13 days ago ·
7g0stsfu
ah-xuc.7 httpapi: Vikunja webhook receiver with HMAC verification
Past Stand
bd reopen ah-xuc.7
| Created by | Eugene Blikh |
| Owner | bigbes@gmail.com |
| Created | 2026-07-13T05:15:43Z |
| Started | 2026-07-13T05:44:26Z |
| Updated | 2026-07-13T06:22:50Z |
| Closed | 2026-07-13T06:22:50Z |
POST /webhooks/vikunja as a reconciler poke. Design principle 1: a webhook only triggers an immediate iteration; polling stays the source of truth because Vikunja delivers webhooks once, without retries.
Deps gain WebhookSecret string and a Webhook hook (non-blocking; cmd wires it to Reconciler.Poke). Empty secret = feature disabled: the route is not registered at all. Verification: X-Vikunja-Signature carries hex HMAC-SHA256 over the raw request body; compute over the exact bytes read and compare with hmac.Equal; missing or wrong signature = 401 with a terse body that echoes nothing back; cap the body at 256 KiB. After verification decode {event_name} best-effort for the log line only — the payload is otherwise untrusted and unused. Respond 200 fast, call the hook once per verified delivery. Exposing the loopback listener to Vikunja is infra (ah-k23), not this bead.
go test ./internal/httpapi/... green, go vet clean; tests: a valid computed signature pokes the hook and returns 200; tampered body, wrong secret, and missing header return 401 without invoking the hook; oversized body rejected; unset secret leaves the route absent and existing routes unaffected
ah-xuc.10
— cmd/agenthubd: wire webhook secret and Telegram notifier
blocks
| id | ah-xuc.7 |
| content_hash | 62a6806491ee4096a8206ec5b958064c69d772d7f8376188ca61bbfc8a354510 |
| title | httpapi: Vikunja webhook receiver with HMAC verification |
| description | POST /webhooks/vikunja as a reconciler poke. Design principle 1: a webhook only triggers an immediate iteration; polling stays the source of truth because Vikunja delivers webhooks once, without retries. Deps gain WebhookSecret string and a Webhook hook (non-blocking; cmd wires it to Reconciler.Poke). Empty secret = feature disabled: the route is not registered at all. Verification: X-Vikunja-Signature carries hex HMAC-SHA256 over the raw request body; compute over the exact bytes read and compare with hmac.Equal; missing or wrong signature = 401 with a terse body that echoes nothing back; cap the body at 256 KiB. After verification decode {event_name} best-effort for the log line only — the payload is otherwise untrusted and unused. Respond 200 fast, call the hook once per verified delivery. Exposing the loopback listener to Vikunja is infra (ah-k23), not this bead. |
| design | |
| acceptance_criteria | go test ./internal/httpapi/... green, go vet clean; tests: a valid computed signature pokes the hook and returns 200; tampered body, wrong secret, and missing header return 401 without invoking the hook; oversized body rejected; unset secret leaves the route absent and existing routes unaffected |
| notes | |
| status | closed |
| priority | 2 |
| issue_type | feature |
| assignee | Eugene Blikh |
| estimated_minutes | NULL |
| created_at | 2026-07-13T05:15:43Z |
| created_by | Eugene Blikh |
| owner | bigbes@gmail.com |
| updated_at | 2026-07-13T06:22:50Z |
| closed_at | 2026-07-13T06:22:50Z |
| closed_by_session | |
| external_ref | NULL |
| spec_id | |
| compaction_level | 0 |
| compacted_at | NULL |
| compacted_at_commit | NULL |
| original_size | NULL |
| sender | |
| ephemeral | 0 |
| wisp_type | |
| pinned | 0 |
| is_template | 0 |
| mol_type | |
| work_type | |
| source_system | |
| metadata | �{} |
| source_repo | |
| close_reason | 61400ae; HMAC discipline verified (full-read-before-verify, uniform 401, constant-time compare); review clean |
| event_kind | |
| actor | |
| target | |
| payload | |
| await_type | |
| await_id | |
| timeout_ns | 0 |
| waiters | |
| hook_bead | |
| role_bead | |
| agent_state | |
| last_activity | NULL |
| role_type | |
| rig | |
| due_at | NULL |
| defer_until | NULL |
| no_history | 0 |
| started_at | 2026-07-13T05:44:26Z |
| is_blocked | 0 |
| id | 2f0edb7d-9d7d-5865-b263-93db7cce7609 |
| issue_id | ah-xuc.7 |
| type | parent-child |
| created_at | 2026-07-13T08:15:43Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 37f3de87-cff8-5310-896f-877fce7a1f82 |
| issue_id | ah-xuc.7 |
| type | blocks |
| created_at | 2026-07-13T08:16:50Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc.1 |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | f115477d-4fe6-54ee-a5a1-fee946cd310f |
| issue_id | ah-xuc.10 |
| type | blocks |
| created_at | 2026-07-13T08:16:54Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc.7 |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 019f59e7-11cc-7d71-a6ba-40ba924b8b16 |
| issue_id | ah-xuc.7 |
| event_type | created |
| actor | Eugene Blikh |
| old_value | |
| new_value | |
| comment | NULL |
| created_at | 2026-07-13T08:15:43Z |
| id | 019f5a01-5987-77cc-a14e-5139a3efbd20 |
| issue_id | ah-xuc.7 |
| event_type | claimed |
| actor | Eugene Blikh |
| old_value | {"id":"ah-xuc.7","title":"httpapi: Vikunja webhook receiver with HMAC verification","description":"POST /webhooks/vikunja as a reconciler poke. Design principle 1: a webhook only triggers an immediate iteration; polling stays the source of truth because Vikunja delivers webhooks once, without retries.\n\nDeps gain WebhookSecret string and a Webhook hook (non-blocking; cmd wires it to Reconciler.Poke). Empty secret = feature disabled: the route is not registered at all. Verification: X-Vikunja-Signature carries hex HMAC-SHA256 over the raw request body; compute over the exact bytes read and compare with hmac.Equal; missing or wrong signature = 401 with a terse body that echoes nothing back; cap the body at 256 KiB. After verification decode {event_name} best-effort for the log line only — the payload is otherwise untrusted and unused. Respond 200 fast, call the hook once per verified delivery. Exposing the loopback listener to Vikunja is infra (ah-k23), not this bead.","acceptance_criteria":"go test ./internal/httpapi/... green, go vet clean; tests: a valid computed signature pokes the hook and returns 200; tampered body, wrong secret, and missing header return 401 without invoking the hook; oversized body rejected; unset secret leaves the route absent and existing routes unaffected","status":"open","priority":2,"issue_type":"feature","owner":"bigbes@gmail.com","created_at":"2026-07-13T05:15:43Z","created_by":"Eugene Blikh","updated_at":"2026-07-13T05:15:43Z"} |
| new_value | {"assignee":"Eugene Blikh","status":"in_progress"} |
| comment | NULL |
| created_at | 2026-07-13T08:44:25Z |
| id | 019f5a24-8416-7c7b-92f8-00b2136410d2 |
| issue_id | ah-xuc.7 |
| event_type | closed |
| actor | Eugene Blikh |
| old_value | |
| new_value | 61400ae; HMAC discipline verified (full-read-before-verify, uniform 401, constant-time compare); review clean |
| comment | NULL |
| created_at | 2026-07-13T09:22:50Z |
| id | 019f7cda-9352-75f7-ab35-d0b10fb89516 |
| issue_id | ah-xuc.7 |
| event_type | label_added |
| actor | Eugene Blikh |
| old_value | NULL |
| new_value | NULL |
| comment | Added label: milestone:stage-2 |
| created_at | 2026-07-20T03:08:47Z |
| id | 019f7cdc-1527-74af-816c-d1050ea95c71 |
| issue_id | ah-xuc.7 |
| event_type | label_removed |
| actor | Eugene Blikh |
| old_value | NULL |
| new_value | NULL |
| comment | Removed label: milestone:stage-2 |
| created_at | 2026-07-20T03:10:25Z |
No comments.
Close reason