[[
wikihub
]]
Search
⌘K
Explore
People
For Agents
Sign in
Explore
People
For Agents
Sign in
@jacobcole / picortex — planning / docs/specs/006-linq-integration.md
Suggest edit
Cancel
Submit suggestion
Title
Name
Note
--- visibility: public --- # Spec 006 — Linq integration **Status:** Draft **Related:** [PRD FR-1..FR-4](../prd/001-picortex-v1.md#linq-integration), [ADR-0004](../adrs/0004-linq-primary-channel.md), [Wiki: linq-protocol](../wiki/linq-protocol.md) ## Goal Speak fluent Linq, both inbound (webhook ingest) and outbound (partner API), with HMAC signing parity. Testable end-to-end against `linq-sim` without needing real phone numbers. ## Inbound `POST /api/linq/inbound` Headers: - `Linq-Signature: t=<unix_ts>,s=<hex_hmac_sha256>` (Cortex's exact shape) - `Linq-Event-Id: <uuid>` - `Content-Type: application/json` Verification: 1. Parse `t` and `s` from `Linq-Signature`. 2. Compute `hmac_sha256("{t}.{raw_body}", LINQ_WEBHOOK_SECRET)`; constant-time compare. 3. Reject if timestamp skew > 5 min (replay guard). 4. Dedup via `Linq-Event-Id` seen within 24h. 5. Log with `X-Request-ID` tagged to event id. Supported event types (from linq-sim): ``` message.received message.delivered message.read message.edited message.failed reaction.added reaction.removed chat.typing_indicator.started chat.typing_indicator.stopped chat.created chat.updated chat.group_name_updated participant.added participant.removed ``` **New for v1:** `message.received` with `data.reply_to_message_id` set (requires [S2 linq-sim PR](../plans/2026-04-23-initial-roadmap.md#s2-linq-sim-thread-support)). Dispatch table: each event → a handler in `src/channels/linq/handlers/*.ts`. Handlers are pure wrt Linq — they call into the core chat service. ## Outbound Single client at `src/channels/linq/client.ts`: ```ts class LinqClient { sendMessage({ chatId, text, replyToMessageId?, attachments? }) createChat({ participants }) getChat({ chatId }) addParticipant({ chatId, participant }) removeParticipant({ chatId, participant }) updateChat({ chatId, patch }) } ``` Base URL from `LINQ_BASE_URL`. For dev, points at linq-sim (`http://127.0.0.1:8447`). Calls to sim still succeed but are captured for inspection in the sim UI. Retry: exponential backoff up to 3 attempts on 5xx. 4xx never retried. ## Channel abstraction ```ts interface Channel { name: string verifyInbound(req): Promise<ParsedEvent> send(msg: OutboundMessage): Promise<{id: string}> supports(feature: "reactions" | "threads" | "typing"): boolean } ``` `LinqChannel` implements this. Future `OpenChatChannel` ([Wiki: openchat-adapter](../wiki/openchat-adapter.md)) will too. ## Testing - **Unit:** HMAC signer/verifier, timestamp skew, signature format parsing. - **Integration:** linq-sim roundtrip — send via sim's admin UI, picortex handles, responds via sim-captured `/api/partner/v3/sendMessage`. - **E2E:** full conversation against linq-sim. ## Open questions - OQ1: Do we store inbound raw event JSON or normalized? (Store raw + normalized both — Cortex does this.) - OQ2: What attachment types must we support at v0.1? (Text-only is fine; images/voice memos are v0.2.) - OQ3: Linq's rate limits?