Your users click. Your function is already awake.
Firebase Cloud Functions cold start in 800ms–2s. Ignite eliminates that by detecting hover intent 150ms before a click and firing a non-blocking warm-up signal through a Rust edge proxy — so by the time the user's finger lifts, the container is hot.
onFocus for keyboard, onTouchStart for mobile)sendBeacon fires a POST to the Rust proxy — non-blocking, zero impact on the userctx.wait_until — returns 200 OK instantly__ignite: true, verifies the secret, exits early — container is now warm| Package | Description |
|---|---|
@farhanmansuri/ignite-core |
Framework-agnostic signal logic. sendBeacon primary, fetch fallback. 5-min TTL warm cache. |
@farhanmansuri/ignite-react |
React hook with stable useRef callbacks, hover/focus/touch support. |
@farhanmansuri/ignite-vue |
Vue 3 composable with onUnmounted cleanup. |
@farhanmansuri/ignite-svelte |
Svelte use:ignite action with full lifecycle cleanup. |
@farhanmansuri/ignite-firebase |
Type-safe early-exit middleware for onCall and onRequest triggers. |
@farhanmansuri/ignite-proxy |
Rust/Wasm Cloudflare Worker. Auth, exact-match allowlist, ctx.wait_until. |
The proxy is a Rust/Wasm Cloudflare Worker that sits between your frontend and Firebase.
# Install Rust + wrangler if you haven't
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
npm install -g wrangler && wrangler login
cd packages/proxy
wrangler secret put IGNITE_SECRET # shared auth key — keep this private
wrangler secret put FIREBASE_BASE_URL # e.g. https://us-central1-your-project.cloudfunctions.net
wrangler secret put ALLOWED_FUNCTIONS # comma-separated: createProject,processPayment,loginUser
wrangler deploy
The allowlist uses exact-match validation —
createProjectwill never matchcreateProjectAdmin.
import { igniteWrapper, igniteMiddleware } from '@farhanmansuri/ignite-firebase';
// onCall
export const createProject = igniteWrapper(async (req) => {
// your logic here — never reached on warm signals
}, process.env.IGNITE_SECRET);
// onRequest
export const processPayment = igniteMiddleware(async (req, res) => {
// your logic here
}, process.env.IGNITE_SECRET);
React
import { useIgnite } from '@farhanmansuri/ignite-react';
const ignite = useIgnite('createProject', {
proxyUrl: 'https://your-proxy.workers.dev',
onWarm: (fn, ms) => console.log(`${fn} warmed in ${ms}ms`),
onError: (err) => console.error(err),
});
<button {...ignite} onClick={handleSubmit}>
Create Project
</button>
Vue 3
<template>
<button
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
@click="handleSubmit"
>
Create Project
</button>
</template>
<script setup lang="ts">
import { useIgnite } from '@farhanmansuri/ignite-vue';
const { onMouseEnter, onMouseLeave } = useIgnite('createProject', {
proxyUrl: 'https://your-proxy.workers.dev',
});
</script>
Svelte
<script>
import { ignite } from '@farhanmansuri/ignite-svelte';
</script>
<button
use:ignite={{ functionName: 'createProject', proxyUrl: 'https://your-proxy.workers.dev' }}
on:click={handleSubmit}
>
Create Project
</button>
X-Ignite-Key header verified on the proxy and re-verified on the Firebase function. Never exposed to the browser.ALLOWED_FUNCTIONS env var on the proxy. split(',').any(|s| s.trim() == name) — no substring bypass possible.{ status: "ignited" } before touching any business logic or incurring billing for real work.Browser
│ mouseenter (150ms)
▼
ignite-react / vue / svelte
│ sendBeacon (non-blocking)
▼
ignite-proxy (Rust · Cloudflare Worker)
│ auth + allowlist + ctx.wait_until
▼
Firebase Function → __ignite: true → early exit, container warm
Most likely cause: The Firebase function container spun down between the warm signal and the click.
hoverTimeout (default 150ms) is appropriate — increase it if users tend to click slowly after hoveringonWarm: (fn, ms) => console.log(...) and check the browser console401 UnauthorizedThe X-Ignite-Key header doesn't match IGNITE_SECRET on the proxy.
wrangler secret listapiKey in the frontend optionswrangler deploy403 ForbiddenThe function name is not in ALLOWED_FUNCTIONS.
wrangler secret list (won't show the value, but confirms it exists)CreateProject ≠ createProjectwrangler secret put ALLOWED_FUNCTIONS then redeploy429 Too Many RequestsRate limit hit — 30 warm signals per IP per 60 seconds.
RATE_LIMIT_MAX in packages/proxy/src/lib.rs and redeploysendBeacon fires but nothing reaches FirebaseThe proxy deployed successfully but the background ctx.wait_until call to Firebase is failing silently.
FIREBASE_BASE_URL is set and correct: wrangler secret listhttps://us-central1-myapp.cloudfunctions.netcreateProject not createProject-v2onWarm is never triggeredsendBeacon doesn't return a response body — onWarm latency is measured from queue time, not execution. This is expected.
If using the fetch fallback (sendBeacon unavailable), onWarm fires after the fetch resolves.
onError fires with AbortErrorThe fetch fallback timed out (default 5 seconds). The proxy may be slow or unreachable.
FETCH_TIMEOUT_MS in packages/core/src/index.ts if your proxy is in a distant regionCORS: Make sure the proxy wrangler.toml is deployed — the Access-Control-Allow-Origin: * header is set by the Worker, not by Firebase.
CSP: If your app uses a Content-Security-Policy, add your proxy URL to connect-src:
Content-Security-Policy: connect-src 'self' https://your-proxy.workers.dev;
IGNITE_SECRETopenssl rand -hex 32wrangler secret put IGNITE_SECRET → enter new value → wrangler deployapiKey option and redeploy your app# Run the proxy locally (requires wrangler + Rust toolchain)
cd packages/proxy
wrangler dev
# Test it directly
curl -X POST "http://localhost:8787/warm?fn=createProject" \
-H "X-Ignite-Key: your-secret" \
-H "Content-Type: application/json" \
-d '{"__ignite": true}'
# Expected: "Ignited"
pnpm install
pnpm build # builds all JS packages
pnpm test # runs all test suites (46 tests)
# Proxy tests (requires Rust)
cd packages/proxy && cargo test