Skip to content

feat(server): mount the Linear webhook ingress /webhooks/linear (RIG-2732 T7) - #744

Merged
mattwilkinsonn merged 3 commits into
mainfrom
compass-forge/rig-2732-t7-linear-mount
Aug 30, 2026
Merged

feat(server): mount the Linear webhook ingress /webhooks/linear (RIG-2732 T7)#744
mattwilkinsonn merged 3 commits into
mainfrom
compass-forge/rig-2732-t7-linear-mount

Conversation

@rigel-mintaka

Copy link
Copy Markdown
Contributor

Adds the shared Linear webhook ingress — POST /webhooks/linear (DL-302, Matt's RIG-2717 amendment) — as part of the forge agent-notification lane assembly. Mounted on the network TLS door, outside the bearer + admin-gate interceptors (Linear signs each delivery with the webhook secret, so the handler's own HMAC verify is its whole authentication), gated on the Linear webhook secret being declared — an App-independent gate: a deployment can run Linear notifications without a GitHub App and vice versa.

What lands

  • NewLinearWebhookHandler + buildLinearWebhookWiring: one shared http.Handler that does a fail-closed raw-body HMAC verify then an inner type-switch — AgentSessionEvent → a local SessionEventSink seam (satisfied later by *linearagent.Dispatcher); Issue/Comment data-change events → a ForgeEventSink data arm.
  • Mount in buildNetworkServer guarded on linearWebhookHandler != nil (undeclared secret → nil handler → no route).
  • Flag --forge-linear-webhook-secret (env COMPASS_FORGE_LINEAR_WEBHOOK_SECRET) + ForgeConfig.LinearWebhookSecretName threading.
  • Status codes mirror the GitHub ingress: 405 / 413 (1 MiB cap) / 400 (bad sig) / 200-drop (stale 60s skew, unparseable, nil sink) / 500 (queue full / Enqueue error).

Both sinks injected-and-nil-for-now (DL-302)

The data-change arm's sink is nil for now by design: feeding the GitHub-coordinate notify+board fanout would mis-route Linear events (Linear subs looked up under a GitHub coordinate), so the data branch acks-and-drops until a Linear-provider-bound notify lane injects a real sink. The session arm is nil until the RIG-2717 responder assembly wires a real *linearagent.Dispatcher. Ten handler tests cover bad-sig, issue/comment create, session event, queue-full, stale-timestamp, nil-session-sink, ignored-type, issue-remove-drop, and nil-data-sink.

Spec-impact: none. Refs RIG-2732, RIG-2717.

Co-authored-by: Matt Wilkinson matt@rigel.build

…2732 T7)

Adds the shared Linear webhook ingress — `POST /webhooks/linear` (DL-302, Matt's RIG-2717 amendment) — as part of the forge agent-notification lane assembly. Mounted on the network TLS door, outside the bearer + admin-gate interceptors (Linear signs each delivery with the webhook secret, so the handler's own HMAC verify is its whole authentication), gated on the Linear webhook secret being declared — an **App-independent** gate: a deployment can run Linear notifications without a GitHub App and vice versa.

### What lands

- `NewLinearWebhookHandler` + `buildLinearWebhookWiring`: one shared `http.Handler` that does a fail-closed raw-body HMAC verify then an inner `type`-switch — `AgentSessionEvent` → a local `SessionEventSink` seam (satisfied later by `*linearagent.Dispatcher`); `Issue`/`Comment` data-change events → a `ForgeEventSink` data arm.
- Mount in `buildNetworkServer` guarded on `linearWebhookHandler != nil` (undeclared secret → nil handler → no route).
- Flag `--forge-linear-webhook-secret` (env `COMPASS_FORGE_LINEAR_WEBHOOK_SECRET`) + `ForgeConfig.LinearWebhookSecretName` threading.
- Status codes mirror the GitHub ingress: 405 / 413 (1 MiB cap) / 400 (bad sig) / 200-drop (stale 60s skew, unparseable, nil sink) / 500 (queue full / Enqueue error).

### Both sinks injected-and-nil-for-now (DL-302)

The data-change arm's sink is nil for now by design: feeding the GitHub-coordinate notify+board fanout would mis-route Linear events (Linear subs looked up under a GitHub coordinate), so the data branch acks-and-drops until a Linear-provider-bound notify lane injects a real sink. The session arm is nil until the RIG-2717 responder assembly wires a real `*linearagent.Dispatcher`. Ten handler tests cover bad-sig, issue/comment create, session event, queue-full, stale-timestamp, nil-session-sink, ignored-type, issue-remove-drop, and nil-data-sink.

Spec-impact: none. Refs RIG-2732, RIG-2717.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@linear-code

linear-code Bot commented Aug 30, 2026

Copy link
Copy Markdown

RIG-2732

RIG-2717

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-forge-rig-2732-t7-li.compass-eng-docs.pages.dev

Deployed from compass-forge/rig-2732-t7-linear-mount at c4f318e.

rigel-mintaka and others added 2 commits August 30, 2026 14:13
…e comments (RIG-2732 T7)

Review follow-up for #744 (the /webhooks/linear mount slice).

Close the test-adequacy gap the review flagged on this internet-facing,
unauthenticated endpoint: four ServeHTTP branches had zero coverage, most
critically the 1 MiB body-cap security control. Add table cases mirroring
github_webhook_test.go:

- non-POST -> 405 (method gate)
- over-cap signed body -> 413 (MaxBytesReader memory-amplification guard)
- secret-resolve fault -> 503 (fail-closed before parse)
- verified-but-unparseable envelope -> 200-drop (no Linear retry on authentic
  malformed body)

All assert zero enqueues. Also fix two stale doc-comment paths on
linearWebhookHandler / NewLinearWebhookHandler (/webhooks -> /webhooks/linear),
matching the linearWebhookPath constant the handler actually mounts at.

Refs RIG-2732

Co-authored-by: Matt Wilkinson <matt@rigel.build>
…ses (RIG-2732 T7)

Round-2 review follow-up for #744.

TestLinearWebhookHandler_BodyTooLarge and _SecretUnavailable each signed a body
that the targeted branch never verifies — MaxBytesReader trips during io.ReadAll
before the HMAC check in the former, and the secret resolver faults before it in
the latter. The signature was inert plumbing a reader could mistake for a
load-bearing part of the assertion. Pass "" with a one-line comment that the
branch fires before verification, so each test is honest about what it exercises.

Refs RIG-2732

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@mattwilkinsonn
mattwilkinsonn marked this pull request as ready for review August 30, 2026 18:55
@mattwilkinsonn
mattwilkinsonn self-requested a review as a code owner August 30, 2026 18:55
@mattwilkinsonn
mattwilkinsonn merged commit 4f539b0 into main Aug 30, 2026
13 checks passed
@mattwilkinsonn
mattwilkinsonn deleted the compass-forge/rig-2732-t7-linear-mount branch August 30, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants