Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Trajectory: Align Relayfile inbound GitHub PR identity matching

> **Status:** ✅ Completed
> **Confidence:** 93%
> **Started:** September 19, 2026 at 08:46 PM
> **Completed:** September 19, 2026 at 08:54 PM

---

## Summary

Added exact GitHub PR inbound matching with HMAC-bound authorization. 1153 engine tests, typecheck, build, and actual cloud Queue-to-signed-inbound-to-agent-inbox proof pass. Deploy receiver before cloud and reprovision with repository read authorization; no production changes.

**Approach:** Standard approach

---

## Key Decisions

### Seal semantic GitHub PR authorization into callback URL and signing secret
- **Chose:** Seal semantic GitHub PR authorization into callback URL and signing secret
- **Reasoning:** Legacy receiver URLs must remain literal; a workspace-key-authorized new target uses a distinct HMAC derivation, so old secrets cannot enable semantic matching by query tampering.

---

## Chapters

### 1. Work
*Agent: default*

- Seal semantic GitHub PR authorization into callback URL and signing secret: Seal semantic GitHub PR authorization into callback URL and signing secret
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"id": "traj_08wdfgur178x",
"version": 1,
"task": {
"title": "Align Relayfile inbound GitHub PR identity matching"
},
"status": "completed",
"startedAt": "2026-09-20T03:46:15.106Z",
"completedAt": "2026-09-20T03:54:42.152Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-09-20T03:54:42.070Z"
}
],
"chapters": [
{
"id": "chap_i6kdencf1bu0",
"title": "Work",
"agentName": "default",
"startedAt": "2026-09-20T03:54:42.070Z",
"endedAt": "2026-09-20T03:54:42.152Z",
"events": [
{
"ts": 1789876482070,
"type": "decision",
"content": "Seal semantic GitHub PR authorization into callback URL and signing secret: Seal semantic GitHub PR authorization into callback URL and signing secret",
"raw": {
"question": "Seal semantic GitHub PR authorization into callback URL and signing secret",
"chosen": "Seal semantic GitHub PR authorization into callback URL and signing secret",
"alternatives": [],
"reasoning": "Legacy receiver URLs must remain literal; a workspace-key-authorized new target uses a distinct HMAC derivation, so old secrets cannot enable semantic matching by query tampering."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Added exact GitHub PR inbound matching with HMAC-bound authorization. 1153 engine tests, typecheck, build, and actual cloud Queue-to-signed-inbound-to-agent-inbox proof pass. Deploy receiver before cloud and reprovision with repository read authorization; no production changes.",
"approach": "Standard approach",
"confidence": 0.93
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relaycast",
"tags": [],
"_trace": {
"startRef": "af4428bf3411ecf6ef944f20a5a7158a81ea7387",
"endRef": "af4428bf3411ecf6ef944f20a5a7158a81ea7387"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Trajectory: Address full PR feedback audit and docstring coverage

> **Status:** ✅ Completed
> **Confidence:** 90%
> **Started:** September 19, 2026 at 09:58 PM
> **Completed:** September 19, 2026 at 09:59 PM

---

## Summary

Added signing, matcher and proof-helper docstrings; 17 tests, typecheck, lint, build and cross-head proof pass. Review readiness remains pending current-head CI and review coverage.

**Approach:** Standard approach

---

## Key Decisions

### Document all touched helpers and audit summaries and annotations as well as inline threads
- **Chose:** Document all touched helpers and audit summaries and annotations as well as inline threads
- **Reasoning:** Green status checks did not capture CodeRabbit docstring warning or rate-limited coverage.

---

## Chapters

### 1. Work
*Agent: default*

- Document all touched helpers and audit summaries and annotations as well as inline threads: Document all touched helpers and audit summaries and annotations as well as inline threads
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"id": "traj_5vufil7zdn8h",
"version": 1,
"task": {
"title": "Address full PR feedback audit and docstring coverage"
},
"status": "completed",
"startedAt": "2026-09-20T04:58:22.609Z",
"completedAt": "2026-09-20T04:59:20.399Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-09-20T04:59:20.221Z"
}
],
"chapters": [
{
"id": "chap_us4lrn3lsslm",
"title": "Work",
"agentName": "default",
"startedAt": "2026-09-20T04:59:20.221Z",
"endedAt": "2026-09-20T04:59:20.399Z",
"events": [
{
"ts": 1789880360222,
"type": "decision",
"content": "Document all touched helpers and audit summaries and annotations as well as inline threads: Document all touched helpers and audit summaries and annotations as well as inline threads",
"raw": {
"question": "Document all touched helpers and audit summaries and annotations as well as inline threads",
"chosen": "Document all touched helpers and audit summaries and annotations as well as inline threads",
"alternatives": [],
"reasoning": "Green status checks did not capture CodeRabbit docstring warning or rate-limited coverage."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Added signing, matcher and proof-helper docstrings; 17 tests, typecheck, lint, build and cross-head proof pass. Review readiness remains pending current-head CI and review coverage.",
"approach": "Standard approach",
"confidence": 0.9
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relaycast",
"tags": [],
"_trace": {
"startRef": "cd23824e552a72596175336ee62ecbe9cf5cc0ca",
"endRef": "cd23824e552a72596175336ee62ecbe9cf5cc0ca"
}
}
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Packages without a separate changelog are covered by the cross-package notes below.

## [Unreleased]
## [Unreleased - Patch]

### Fixed

- GitHub PR inbound subscriptions follow title changes and receive associated comments and reviews without widening repository or PR scope.

## [8.11.4] - 2026-09-19

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ Relaycast gives your agents shared channels, threads, DMs, reactions, files, sea

See the [changelog](CHANGELOG.md) for release highlights and upgrade notes.

Relayfile inbound targets scoped to `/github/repos/<owner>/<repo>/pulls/<number>/**`
follow GitHub PR title changes and associated comments/reviews with the exact stable
PR reference. New targets carry a server-issued, HMAC-bound opt-in; legacy targets
retain literal matching. Deploy the Relaycast receiver and Relayfile cloud
companion, then reprovision the binding with full repository read permission.
Other globs retain literal path matching.

## Quick Start

Install:
Expand Down
12 changes: 12 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5921,6 +5921,11 @@ paths:
description: >
Provision a relayfile inbound callback URL and derived HMAC secret for a
workspace channel/provider/path binding.
Numeric GitHub PR subtree bindings also match titled PR directories and
associated provider-sync events with the exact stable PR resource reference
in the same repository. Other path globs retain literal matching.
This requires a newly provisioned target with a server-issued HMAC-bound
github_pr_identity opt-in; legacy target URLs remain literal.
tags:
- Relayfile
security:
Expand Down Expand Up @@ -5995,6 +6000,13 @@ paths:
required: true
schema:
type: string
- name: github_pr_identity
in: query
required: false
schema:
type: string
enum: ['1']
description: Server-issued opt-in sealed into the target signing secret. Do not add to legacy URLs; reprovision the target with a workspace key.
- name: X-Relay-Timestamp
in: header
required: true
Expand Down
6 changes: 5 additions & 1 deletion packages/engine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ See the [root changelog](../../CHANGELOG.md) for cross-package release highlight
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [Unreleased - Patch]

### Fixed

- Relayfile numeric PR subtree targets accept titled GitHub PR paths and related provider events carrying the exact stable PR reference.

## [8.11.4] - 2026-09-19

Expand Down
51 changes: 51 additions & 0 deletions packages/engine/scripts/prove-github-cloud-inbound.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { createHmac } from 'node:crypto';
import { createEngine } from '../dist/engine.js';
import { createNodeRuntime } from '../dist/adapters/node/index.js';

// Consume actual Queue payloads exported by relayfile-cloud's Workerd proof.
// Build the engine and set RELAYFILE_PROOF_EVENTS to that JSON artifact first.
assert.ok(process.env.RELAYFILE_PROOF_EVENTS, 'RELAYFILE_PROOF_EVENTS is required');
const events = JSON.parse(await readFile(process.env.RELAYFILE_PROOF_EVENTS, 'utf8'));
assert.equal(events.length, 5);
const runtime = createNodeRuntime({ dbPath: ':memory:', baseUrl: 'http://localhost:0', migrate: true,
config: { environment: 'test', relayfileInboundSecret: 'fixture-master' },
presence: { sweepIntervalMs: 0 }, eventQueue: { pollIntervalMs: 0 } });
runtime.webhookQueue.stop();
const app = createEngine(runtime.deps);
/**
* Call the in-memory engine with an optional bearer token and JSON body.
* Fail the proof on HTTP errors and return the successful response data.
*/
async function request(path, token, body) {
const response = await app.request(path, { method: body ? 'POST' : 'GET',
headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) },
...(body ? { body: JSON.stringify(body) } : {}) });
assert.ok(response.ok, `request failed: ${response.status}`);
return (await response.json()).data;
}
try {
const ws = await request('/v1/workspaces', null, { name: 'cloud-inbound-proof' });
const key = ws.api_key ?? ws.key;
const agent = await request('/v1/agents', key, { name: 'subscriber' });
await request('/v1/channels/general/join', agent.token, {});
const target = await request('/v1/integrations/relayfile/inbound-target', key, {
channel: 'general', provider: 'github', path_glob: '/github/repos/AgentWorkforce/relay/pulls/1815/**',
});
const delivered = [];
for (const event of events) {
const body = JSON.stringify(event);
const timestamp = String(Math.floor(Date.now() / 1000));
const response = await app.request(target.url, { method: 'POST', body, headers: {
'content-type': 'application/json', 'X-Relay-Event-Id': event.eventId,
'X-Relay-Timestamp': timestamp,
'X-Relay-Signature': createHmac('sha256', target.secret).update(`${timestamp}.${body}`).digest('hex'),
} });
assert.equal(response.status, 201, `cloud event skipped: ${event.eventId}`);
delivered.push((await response.json()).data.message_id);
}
const inbox = await request('/v1/deliveries', agent.token);
assert.equal(inbox.filter(item => delivered.includes(item.message_id)).length, 5);
console.log('PASS: 5 real cloud Queue events reached the signed receiver and subscriber inbox');
} finally { runtime.close(); }
Loading
Loading