RFC · Design guide

Request for comments

A proposal written early enough for review to change the design.

An internal engineering RFC template covering context, scope, design, alternatives, risks, rollout and operation, open questions, and the recorded outcome.

Download rfc-template.md ↓View the Markdown example ↓

At a glance

Also called
Internal design proposal
Written by
RFC steward, with input from affected engineers and operators
Ideal length
As short as the decision allows
Shelf life
Active during review; retain the outcome and rationale, and add normative supersession links only after acceptance

This guide uses “RFC” in the internal engineering sense. It describes an internal proposal, not a document published in the RFC Series. The process below is local guidance informed by the listed sources; it is not a universal RFC standard.

An internal RFC makes the problem, goals, proposed design, alternatives, drawbacks, operational impact, and unresolved questions visible. Review should improve the proposal and end in a recorded outcome. Acceptance records the decision; it enables implementation only when the outcome explicitly says so, and never guarantees priority, assignment, or release.

A draft may state `Proposes to supersede: RFC-ID` to show intent. Update `Supersedes` and `Superseded by` only after an RFC successor is accepted. An RFC can link an ADR, but cannot supersede it.

Make the trade-offs reviewable before implementation.

Technical references

Use these only when the change needs the deeper contract. Keep the main document focused on its decision.

  • Signed webhook ingress

    A focused contract for accepting, authenticating, recording, and safely acknowledging signed webhook deliveries.

  • Reliable event processing

    A compact design reference for durable event receipt, retries, ordering, and safe external effects.

Markdown · rfc-042-webhooks.md

Compact Markdown example

# RFC-042: Accept signed webhooks for partner event ingestion

Illustrative example · fictional data · status: In review · steward: mira@ · decision authority: dan@ (platform lead) · reviewers: rene@ (security), iman@ (integrations), lee@ (operations), tori@ (privacy) · review: 2026-08-28 to 2026-09-04 · acceptance authorises: if accepted, implementation for the one-partner pilot only; no cutover or scale · proposes to supersede: none · supersedes: none · superseded by: none · tracking: INGEST-314 · related: INC-204

## Summary

Our 14 partner APIs are polled every 60 seconds; 98.6% of polls are empty. This RFC proposes signed webhooks with one shared receipt and effect path.

Decision: approve a one-partner, seven-day parallel pilot. It is evidence only: no polling reduction, cutover, or scale. dan@ must separately approve cutover after reconciliation; scale needs 30 days of outcomes.

## Problem

Polling adds delay and cost. The change must not lose, duplicate, accept unverified, or apply stale effects.

## Goals and non-goals

- Reduce latency and empty calls without losing completeness.
- Authenticate delivery, retain evidence, and make retries safe.
- Reconcile, pause, and recover safely.
- Unqualified partners stay on polling; unsafe effects are excluded.

## Proposed design

### Partner contract and ingress

Each enabled partner has an HTTPS endpoint and versioned contract. iman@ records authenticated identity, event-ID scope, signing, replay, limits, source access, and ordering. rene@ approves it; lee@ owns the runbook and evidence. A partner without stable identity, replay, and backfill stays on polling.

The receiver creates no receipt or effect until it verifies the provider contract. It commits the receipt and outbox before `2xx`, and never makes a downstream call in the request path. The detailed ingress rules are in [signed webhook ingress](/design/rfc/signed-webhooks/).

### One receipt and effect path

Webhook, polling, and approved backfill establish the same `canonical_event_key`: `{partner, verified_source_account, provider_event_id}`. The contract defines a versioned fingerprint that is equal across paths; raw signed bytes remain the signature input. Transport is provenance, never key material.

One shared receipt is unique on that key and stores the first fingerprint. A matching fingerprint is a duplicate; a different one is quarantined. Its transaction creates or finds the shared logical effect: event key, effect type, target, and business version. All routes and retries use its recipient idempotency key. Polling is observation-only or uses this path. Backfill uses an authenticated source-query adapter, then this path.

### Recovery, ordering, and data

The worker retries only known transient failures. A timeout becomes `unknown`. Continue only on authoritative, final proof of no effect for the same recipient key; otherwise keep `unknown` for recovery.

For ordered work, the recipient must enforce a monotonic fence or irrevocable no-effect tombstone. Otherwise exclude it or make it safely commutative. Keep replay material separate from logs for the approved period. See [reliable event processing](/design/rfc/reliable-event-processing/).

### Acceptance tests

The automated suite must prove:

1. A valid signed event commits one receipt and outbox item before acknowledgement.
2. Invalid input creates neither receipt nor effect.
3. A cross-route duplicate creates one effect; a conflict is quarantined.
4. A post-commit crash loses no event or duplicates no effect.
5. A late or unknown result cannot regress or repeat an effect.
6. An omitted source event uses the shared backfill path.

## Alternatives and prior art

More frequent polling adds calls and still leaves polling delay. Partner-hosted queues are an option only where a provider offers one. The status quo keeps weak recovery evidence.

## Risks and mitigations

Webhooks add a public endpoint, bursts, duplicates, invalid signatures, and sensitive payloads. Rate-limit partners, reject unsupported input, test key changes, retain polling and reconciliation until cutover, and hold on mismatch or unknown effect.

## Rollout and operation

Deploy behind a per-partner flag. Before the pilot, record 30 UTC days of qualified-event retrieval calls: polling, reconciliation, and backfill.

Run one low-volume partner for seven days. Reconcile daily and at the end. The eligible population is every qualified provider event in the UTC pilot interval after documented source-publication lag. The event-to-effect map defines required effects; reconciliation compares it with shared receipt and effect records. Backfill missing events through the shared path and retain source access through the final check.

lee@ collects evidence. The pilot passes only if the six tests pass; the eligible population has zero missing or duplicate required effects; at least 100 real events and 10 of each type are checked; one authorised delivery completes end to end; and p95 receipt-commit latency is below two seconds. It is first byte to committed receipt for valid first webhook deliveries. Missing evidence, a mismatch, an unknown effect, or a missed limit is a hold.

If it passes, dan@ may record separate cutover authorisation with the polling change, rollback, evidence, and owner. A failed reconciliation or health check stops enablement, restores or retains polling, and reconciles work.

For 30 UTC days after cutover, count the same call classes. Scale needs a 90% reduction against the baseline, complete reconciliation, and zero missing or duplicate effects. Only then may dan@ enable the next partner.

## Open questions

- Partner qualification: iman@, 2026-09-01; otherwise stay on polling.
- Effect idempotency and order: kai@, 2026-09-02; otherwise exclude.

## Decision outcome

In review. If accepted, this RFC authorises only the implementation needed for the recorded one-partner pilot, subject to normal security, change, and release approvals. It does not authorise cutover or scale.

When review closes, dan@ records the decision, date, rationale, material dissent, and follow-up links. A later RFC may supersede this one only after it is accepted; an ADR can be linked as context or a durable result, but cannot be superseded by this RFC.

Detailed guidance

Use it when

  • A public or cross-team contract will change
  • The design is hard or expensive to reverse
  • Material alternatives need an explicit technical decision
  • The change has material security, reliability, operational, compatibility, or migration implications

Skip it when

  • A contained fix or refactor fits into normal code review
  • A reversible experiment can answer the question more directly
  • The change is already covered by an accepted decision or standard
  • The goal is to record a decision already made — use an ADR only if a concise, durable decision record adds value

What it contains

Metadata and decision authority
Status, steward, decision authority, reviewers, review window, implementation tracking, and any accepted RFC-to-RFC supersession link.
Summary
The problem, recommendation, main trade-off, and exact decision requested.
Context, goals and non-goals
Evidence, constraints, desired outcome, and scope before selecting a solution.
Proposed design
Behaviour, interfaces, data, failure modes, compatibility, and enough detail to assess the design.
Alternatives and prior art
Viable options, the status quo, relevant precedent, and why they are not preferred.
Risks and mitigations
Relevant security, privacy, reliability, performance, cost, or operational risks.
Rollout and operation
Migration, testing, enablement, observability, ownership, rollback, Go, Hold, or Stop gates, and operational pause paths.
Specialist records
Linked webhook, event-processing, payment, rollout, and other specialist evidence for each applicable concern.
Open questions
Questions that must be answered or explicitly deferred before the RFC closes, each with an owner, date, and safety boundary.
Decision outcome
Whether the RFC was accepted, rejected, withdrawn, postponed, or superseded, what acceptance authorises, and the rationale and follow-up links.

Write it well

  • Name the decision authority, required reviewers, review window, decision requested, and what acceptance authorises
  • Make motivation, drawbacks, alternatives, and unresolved questions explicit
  • Revise the proposal visibly as feedback changes the design
  • Resolve or record material objections before the decision authority records an outcome
  • Link the implementation tracker without confusing an accepted decision with completed implementation or release
  • Use `Proposes to supersede` only as non-normative RFC-to-RFC draft or review intent; after acceptance, clear it and update `Supersedes` and `Superseded by` on both accepted RFC records together
  • Add an ADR only when a short, enduring decision record adds value

Avoid

  • Present a committed implementation as an open proposal
  • Treat an internal RFC as an RFC Series publication, or assume every published RFC is an Internet Standard
  • Require unanimity or treat silence as agreement on a material risk
  • Assume acceptance guarantees priority, assignment, implementation, or release
  • Make a material change to a closed decision without a linked, durable follow-up record — an RFC, ADR, or maintained equivalent — that records the new evidence or constraint
  • Set `Supersedes` or `Superseded by` before the RFC successor is accepted, use either field for an ADR, or leave one side of a normative RFC supersession link