← All showcases
cross-stack file refsEmbedded ↔ backend
integrationcross-stackembedded

Firmware meets the cloud

10 Hz on the device, 50 ms on the ingest path — one invariant neither codebase can hold alone.

The contract lives in .rq because C and TypeScript cannot see each other.

contracts/ingest.rq

sensor_sample_rate {
    device samples at 10 Hz
    implemented in ["./firmware/adc.c".sample_loop]
    @tags (
        firmware
        timing
    )
}

api_ingest {
    backend must accept a burst within 50 ms
    implemented in ["./api/ingest.ts".ingestBatch]
    aligns with [sensor_sample_rate]
    @tags (
        backend
        sla
    )
}

ingest_contract (
    sensor_sample_rate,
    api_ingest
)

firmware/adc.c

/* rq:["../contracts/ingest.rq".sensor_sample_rate] */
void sample_loop(void) {
    const uint32_t period_ms = 100; /* 10 Hz */
    for (;;) {
        adc_read(&sample);
        queue_push(&sample);
        sleep_ms(period_ms);
    }
}

api/ingest.ts

// rq:["../contracts/ingest.rq".api_ingest]
export async function ingestBatch(events: Event[]) {
  const deadline = Date.now() + 50;
  await queue.push(events, { deadline });
}

in the editor

  • Inbound @referenced-by inlay on sample_loop and ingestBatch
  • Go-to-definition from either side into the shared .rq idea
  • Local graph centered on ingest_contract shows both stacks