Architecture Design Document · rev 1.0 · 2026
FederaQ
A Federated Query Synthesis Engine for Heterogeneous Enterprise Operational Systems
Author · Siddharth Rao
Credentials · TOGAF EA · GCP CA · MLE · Gen AI Leader
Phase · TOGAF ADM Phase B + Phase C
Status · Design Complete · Build Q2 2026
Abstract

Enterprise operational teams routinely pose queries whose answers are distributed across heterogeneous systems of record — CRM pipelines, ERP fulfilment data, IT ticketing queues — with no single system capable of answering them and no indexable document that captures their join. This document describes FederaQ, a federated query synthesis engine that classifies natural language query intent, decomposes queries into per-system sub-queries, dispatches them in parallel to heterogeneous enterprise connectors, enforces a freshness policy against a TTL-keyed cache, and synthesises grounded responses via LLM. The architecture is explicitly distinguished from retrieval-augmented generation (RAG): FederaQ performs no vector retrieval against an indexed corpus. Its retrieval primitive is a live API call against an operational system of record at query time, making freshness a function of source system state rather than index staleness — a load-bearing architectural distinction. All connectors are implemented as temporally mutable stateful simulators, enabling full exercise of the freshness and cache invalidation logic. API credentials are isolated behind a Google Cloud Run service, fronted by Google API Gateway with Firebase Authentication — the browser client never holds secrets, and Cloud Run itself is not publicly invocable. All design decisions are recorded as ADRs. The system runs entirely on GCP's Always Free tier at demo scale, with a hard budget cap on the underlying Gemini billing account, and is documented with a three-stage scaling graduation path.

federated query synthesis connector federation intent classification freshness policy TTL cache Cloud Run + API Gateway proxy Toolformer pattern eventual consistency TOGAF ADM Phase B/C Gemini · AI Studio GCP Always Free tier
3
Enterprise systems federated per query
<2s
P50 synthesis latency · happy path
See §03.1 latency model
$0
Demo infrastructure cost
Free-tier stack · §03.5
11
Architecture decisions recorded as ADRs
§01 Motivation

The federation gap in enterprise operational data

Between 2022 and 2025, enterprise teams broadly adopted retrieval-augmented generation as a solution to LLM hallucination. The predominant implementation pattern — documents chunked, embedded into a vector store, retrieved via cosine similarity — handles document-level static knowledge adequately. Thakur et al. demonstrated on the BEIR benchmark that dense retrievers underperform BM25 on out-of-domain corpora by 11.7 NDCG points on average,[1] a gap that widens when the query targets operational state rather than indexed text.

Operational enterprise queries — "which at-risk deals have fulfilment issues?", "how many open P1 tickets do we have right now?" — require current state from systems of record, not historical text from indexed documents. The answer to such a query does not exist as a document. It exists as a join across live API endpoints that were never designed to communicate with each other. Dresner Advisory Services estimates that enterprise analysts spend 30–45 minutes per day context-switching across systems to manually assemble answers that cross system boundaries.[2]

Three developments make a systematic federated synthesis approach tractable as of 2026. First, Schick et al.'s Toolformer demonstrated that LLMs can reliably self-direct API invocations from natural language input with minimal fine-tuning,[3] establishing the theoretical basis for LLM-directed connector routing. Second, Gemini's two-tier model family — a lightweight Flash-Lite tier for high-volume routing decisions and a stronger Flash tier for reasoning-visible synthesis — makes cost-appropriate multi-step LLM orchestration viable against the Gemini Developer API at negligible per-query cost.[4] Third, Promise.all() enables parallel async fan-out to multiple connectors with latency bounded by the slowest, not the sum, regardless of whether the fan-out runs in the browser or, as this revision requires, inside a Cloud Run service.

Figure 1 — The cross-system synthesis gap · why no single system of record answers the query
NATURAL LANGUAGE QUERY "At-risk deals with fulfilment issues?" CRM · Salesforce Deal stage · owner close date · risk flag ✕ Cannot see fulfilment ERP · SAP Inventory · fulfilment SKU status · allocation ✕ Cannot see deal stage Ticketing · Jira Open tickets · priority SLA status · assignee ✕ Cannot see inventory FederaQ · Federated Query Synthesis · dispatches all three simultaneously via Promise.all() ↑ The answer requires all three. No single SoR contains the join. FederaQ synthesises across all three simultaneously.
§02 Problem Statement

What document-indexed retrieval cannot answer

The failure mode of document RAG on operational enterprise queries is not noisy — it is silent. A document retrieval system returns an answer grounded in indexed text. When the indexed text is a policy guide or a sales playbook, the retrieval produces a fluent, confident response to a question that required live operational data. The document was the wrong unit of truth.

Guu et al. established that open-domain QA systems relying on static corpora degrade predictably when the query targets time-sensitive entity state — a finding that extends directly to enterprise operational queries where the correct answer is a function of current system state, not historical documentation.[5] FederaQ addresses this class of query exclusively; static document lookup is explicitly out of scope.

2.1 — Query type taxonomy and failure modes

Query TypeExampleDocument RAGFederaQ
Cross-system synthesis
multi-source
"At-risk deals with fulfilment issues this quarter?"Fails — no join existsCore use case
Live operational status
point-in-time
"How many open P1 tickets do we have right now?"Stale answerLive connector
Inventory & fulfilment
ERP-grounded
"Which SKUs are below reorder threshold today?"No document existsERP connector
Pipeline health
CRM-grounded
"How is Q2 tracking against target by stage?"Stale if indexedCRM connector
Document lookup
static knowledge
"What is our refund policy?"Handles correctlyOut of scope
Scope boundary
FederaQ addresses queries with a freshness requirement — where the correct answer is a function of current source system state, not historical documentation. Enterprises that index live Salesforce report exports or real-time SAP exports as documents partially mitigate this for low-frequency data. FederaQ addresses the residual case where query-time source state is required and index staleness is operationally unacceptable.
§03 Architecture

Six layers. One query. Grounded synthesis.

FederaQ is structured as six discrete layers. Each layer has a single responsibility. The boundary between layers is a well-defined interface. Layer 2 (Proxy) is a new architectural addition that resolves the credential isolation requirement: the browser client never holds API keys. The proxy layer is now realised as a Cloud Run service, fronted by Google API Gateway, which validates a Firebase-issued Google Sign-In JWT before any request reaches Cloud Run — Cloud Run itself grants run.invoker only to the Gateway's service account, never to allUsers. All upstream calls to Gemini and, in production, to enterprise connectors, are mediated through this layer.

Figure 2 — Six-layer architecture · layer responsibilities and interface boundaries
LAYER 1 APPLICATION NL query bar · Adaptive response area (table / chart / card / prose) Source attribution panel · Connector status indicators · React / static HTML → NL query LAYER 2 PROXY ✦ NEW Cloud Run + API Gateway · Firebase Auth JWT validation · API key never in browser Gemini key in Secret Manager · run.invoker scoped to Gateway SA · scale-to-zero → auth'd request LAYER 3 QUERY INTEL Intent Classifier → Query Planner (rule-based template) → Sub-query generator Routes to connectors · Determines output format · Gemini 3.1 Flash-Lite · JSON schema output → sub-queries/connector LAYER 4 CONNECTOR FED CRM connector · ERP connector · Ticketing connector · stateful mutable simulators Standard interface: query(intent, filters) → {data, source, fetched_at} · Promise.all() parallel → raw results + metadata LAYER 5 FRESHNESS TTL policy per query type · High-sensitivity: bypass cache · Timestamps surfaced to UI Firestore Native mode + TTL (Always Free) · Consistency window check · Staleness warning at skew > 120s → cached / live results LAYER 6 SYNTHESIS Merge connector results · Gemini 3.5 Flash · Format selection · Source citation Adaptive UI render · TABLE / CHART / CARD / PROSE · format confidence threshold fallback → grounded response ✦ NEW Layer 2 (Proxy) is new in this revision — it resolves the credential isolation requirement identified in the architecture review.

3.1 — End-to-end latency model

The following latency budget is derived from documented component benchmarks. Gemini Flash-Lite and Flash token-generation figures are from Google's published Gemini Developer API benchmarks.[4] API Gateway adds a JWT-validation hop in front of Cloud Run; Cloud Run itself scales from zero on a cold start of roughly 1–2s for a small Node container, amortised to near-zero on warm invocations within the same session.[6] All figures are P50 estimates; P99 analysis follows.

Figure 3 — Latency budget · happy path P50 · component-level breakdown
10ms →GW ~200ms Intent classify · Gemini Flash-Lite ~600ms · 3 connectors in parallel (Promise.all) CRM ERP Ticketing 20ms ~400ms · Synthesis · Gemini Flash T=0 ~1.24s P50 55ms 255ms 855ms 875ms P99 degraded path: single slow connector (2s) → total ~3.2s. Timeout policy: 3s per connector, graceful partial-result degradation.

3.2 — Connector federation model

Figure 4 — Connector interface and parallel execution · standard interface enabling heterogeneous federation
QUERY PLANNER rule-based template STANDARD INTERFACE query(intent, filters) → {data, source, fetched_at, ttl_policy} real-API-swappable Promise.all() · latency = max(C1,C2,C3) CRM CONNECTOR Salesforce-schema · deals · stages · owners ⟳ state mutates every 20s ERP CONNECTOR SAP-schema · inventory · fulfilment · SKUs ⟳ inventory decrements every 15s TICKETING CONNECTOR Jira-schema · tickets · priority · SLA ⟳ priority escalates every 30s ⟳ Connectors are stateful and temporally mutable — state drifts on a deterministic seed schedule, exercising the TTL cache layer in all demo scenarios.

3.3 — Adaptive output format selection

Resolution · H4
Format selection now uses a confidence threshold fallback: if the LLM format signal is absent or below threshold, the system applies a deterministic rule — TABLE for any query activating more than one connector, PROSE only for single-connector scalar queries. This removes the contradiction between PROSE fallback and cross-system usability requirements.
Figure 5 — Intent → output format mapping with confidence threshold fallback
INTENT SIGNAL from Gemini response TABLE|CHART|CARD|PROSE CONFIDENCE GATE signal present? confidence ≥ 0.70? USE LLM FORMAT TABLE / CHART / CARD DETERMINISTIC FALLBACK multi-connector→TABLE · single→PROSE ADAPTIVE RENDER DOM delegation YES NO

3.4 — Technology stack

ComponentChoiceAlternativeRationale
LLM inferenceGemini 3.1 Flash-Lite + Gemini 3.5 FlashVertex AI GeminiAI Studio Developer API key, no IAM setup; Vertex AI is the documented production upgrade for SLA + VPC-SC [4]
Credential proxyCloud Run + Google API GatewayCloud Functions (2nd gen)Scale-to-zero, Always Free 2M req/mo; API Gateway validates Firebase JWTs before Cloud Run is reachable [6]
AuthFirebase Authentication · Google Sign-InIdentity PlatformZero-config with Firebase Hosting; sufficient for single-tenant portfolio scope
Intent classificationGemini JSON-schema responseFine-tuned BERTNative responseSchema constrains output structurally, not just by prompt instruction; deterministic at temp=0.0
Query plannerRule-based templateSecond LLM callRemoves ~180ms from critical path; fully deterministic and testable
ConnectorsStateful mutable simulatorsLive Salesforce/SAP/JiraExercises TTL cache under real conditions; real-API-swappable interface
Cache layerFirestore Native mode + TTLMemorystore (Redis)Memorystore has no free tier at all; Firestore's Always Free daily quota (50k reads/20k writes) covers demo traffic at $0
SecretsSecret ManagerCloud Run env var (plaintext)Gemini API key injected at deploy time; never in source control or client code
FrontendStatic HTML + vanilla JSReact / Next.jsZero build step; Firebase Hosting compatible
HostingFirebase Hosting (Spark)Cloud Storage + CDNGenuinely free — no billing account required for this layer; global CDN included
§04 Security Architecture

Credential isolation via Cloud Run + API Gateway + Firebase Auth

Resolution · C3 — API Key Exposure
The original design held the Groq API key in browser client code — acknowledged as a vulnerability in ADR-002 with no resolution path. This revision introduces a Google API Gateway in front of a Cloud Run service as a mandatory trust perimeter, with Firebase Authentication (Google Sign-In) gating who can reach it at all. The browser client never holds secrets of any kind, and never receives a response from an unauthenticated caller.
Figure 6 — Trust boundary model · Firebase Auth → API Gateway → Cloud Run → Gemini
UNTRUSTED ZONE BROWSER CLIENT NL query + Google Sign-In ID token only No API keys held HTTPS to Gateway only AUTH CHECKPOINT API GATEWAY Validates Firebase issued JWT (securetoken .google.com issuer) Rejects unsigned/invalid requests before Cloud Run forwards claims via header TRUST PERIMETER CLOUD RUN Gemini key from Secret Manager Email allowlist check run.invoker → Gateway SA only, never allUsers scale-to-zero europe-west2 GEMINI API Flash-Lite + Flash AI Studio Dev API Server-to-server only query + JWT claims + key Gemini key never crosses this boundary — Firebase JWT is the only credential the browser ever holds

4.1 — Cloud Run allowlist check

API Gateway validates the Firebase-issued JWT before a request ever reaches Cloud Run — that validation is declared in the Gateway's OpenAPI spec, not application code. Cloud Run still applies its own authorisation check on the forwarded identity, matching the least-privilege pattern used in the reference builds:

// server.ts · Cloud Run · federaq-service
// Gemini key comes from Secret Manager via env var — never in source, never in client code

function requireAllowlist(req, res, next) {
  // API Gateway already validated the Firebase JWT; it forwards the
  // decoded claims in this header. Cloud Run trusts Gateway, not the caller.
  const userInfoHeader = req.headers['x-apigateway-api-userinfo'];
  if (!userInfoHeader) {
    return res.status(403).json({ error: 'Missing identity header' });
  }

  const claims = JSON.parse(Buffer.from(userInfoHeader, 'base64url').toString());
  const allowlist = process.env.ALLOWED_EMAILS.split(',');
  if (!allowlist.includes(claims.email)) {
    return res.status(403).json({ error: 'Not authorized: ' + claims.email });
  }
  next();
}

app.post('/query', requireAllowlist, async (req, res) => {
  // Gemini call happens only after both checks pass — Gateway's JWT
  // validation, then this service's own allowlist
  const classification = await classifyIntent(req.body.query);
  // ... connector fetch, cache, synthesis — unchanged from §03
});
§05 Freshness Policy

TTL policy with stateful connector mutation

Resolution · C4 — Mock connectors invalidating freshness policy
Connectors are no longer static JSON objects. Each connector is a stateful, temporally mutable simulator: state is seeded at page load and mutated on a deterministic schedule. The freshness policy is therefore exercised under real conditions — TTL misses trigger live re-fetches that may return changed values.

5.1 — TTL value derivation

TTL values are not arbitrary. Each value is derived from documented source-system update frequency characteristics and operational staleness tolerance for the query type. The following derivations apply to the design phase; production values would be empirically calibrated against actual connector event logs.

Query TypeTTLDerivation basisSource
Inventory60s SAP MM standard batch posting cycle in SMB deployments: 60–300s. 60s represents the aggressive polling end prior to webhook integration. [7]
Live tickets90s Jira webhook delivery SLA documented at up to 60s latency. 90s provides a safety margin over the documented delivery bound. [8]
Deal stage5min CRM deal stage updates are human-initiated; Salesforce Streaming API push latency is 1–3s but human update frequency is measured in minutes to hours. 5min staleness is operationally acceptable for pipeline review queries. [9]
Account info30min Account metadata (owner, segment, contract tier) changes infrequently. Salesforce account record audit logs show median update intervals of 2–4 hours in SMB accounts. [9]

5.2 — Cache routing decision tree

Figure 7 — Cache vs. live routing logic · TTL policy per query type · revised with staleness warning trigger
Query arrives at connector layer Cache hit? valid TTL in Firestore NO YES LIVE SOURCE FETCH update cache + TTL stamp fetched_at may return mutated value High-sensitivity query type? YES · bypass → Force live fetch NO · serve TTL SERVE FROM CACHE TTL valid · low cost surface age to UI check consistency window TTL POLICY MAP inventory → 60s · tickets → 90s · deal stage → 5min · account info → 30min

5.3 — Stateful connector mutation model

// Stateful connector — ERP inventory simulator
// State mutates on a deterministic schedule, exercising real TTL events

class ERPConnector {
  constructor() {
    // Seed from load timestamp — deterministic, reproducible
    this.seed = Date.now();
    this.state = this.initState();
    // Mutate every 15 seconds — faster than 60s TTL, guarantees TTL miss events
    setInterval(() => this.mutate(), 15000);
  }

  mutate() {
    // Deterministic delta: decrement SKU quantities, flip reorder flags
    this.state.skus = this.state.skus.map(sku => ({
      ...sku,
      quantity: Math.max(0, sku.quantity - Math.floor((Date.now() % 7))),
      below_reorder: sku.quantity < sku.reorder_threshold
    }));
    this.state.last_mutated = Date.now();
  }

  query(intent, filters) {
    return {
      data: this.state,
      source: 'ERP · SAP',
      fetched_at: Date.now(),   // stamped at query time, not mutation time
      ttl_policy: 60           // seconds
    };
  }
}
§06 Consistency Model

Eventual consistency with explicit staleness signalling

Resolution · H3 — Consistency and transaction semantics
FederaQ operates under read-your-writes eventual consistency within a single session, with no cross-session or cross-user consistency guarantees. This is now an explicit design constraint, not an omission. The consistency window check and staleness warning are first-class components of the synthesis layer.

The system queries three connectors via Promise.all() at a point in time. Each connector returns its own fetched_at timestamp. When connector results are temporally inconsistent — for example, when ERP inventory state reflects a cache hit from 55 seconds ago while the CRM deal stage is live — the synthesis may join data from different operational moments. In enterprise operational contexts, this can produce materially incorrect conclusions for time-sensitive decisions.

The consistency model below defines the system's formal consistency posture, the consistency window threshold, and the handling rule for skew violations.

Consistency model
Read-your-writes eventual consistency
Within a single session. No cross-session or cross-user guarantees.
Consistency window
120 seconds max temporal skew
Maximum acceptable delta between earliest and latest fetched_at across connectors in a single synthesis call.
Skew violation response
Staleness warning surfaced to UI
Synthesis proceeds; user is informed of the temporal skew and the age of each connector result.
Freshness signal
fetched_at timestamp per connector
Surfaced in source attribution panel for every response. User can inspect data age per source.
Figure 8 — Consistency window check · temporal skew detection and UI warning trigger
T-300s NOW CRM · fetched_at T-20s LIVE ERP · fetched_at T-55s CACHE HIT (within TTL) Ticketing · fetched_at T-160s STALE · exceeds 90s TTL Skew = 140s > 120s threshold → STALENESS WARNING ✓ CRM ↔ ERP skew = 35s within 120s window · OK ⚠ Ticketing data is 160s old surfaced in source attribution panel
§07 Query Intelligence

Intent classifier, rule-based planner, and failure taxonomy

Resolution · H1 + H2 — Classifier failure taxonomy and Query Planner specification
The classifier now has a documented failure taxonomy with explicit handling paths for each case. The Query Planner is specified as a rule-based template engine over classifier output — removing a second LLM call from the critical path (~180ms saving) and making the planner fully deterministic and testable.

7.1 — Classifier prompt (full specification)

Change from design phase
Groq's Llama 3.1 8B had no native structured-output mode, so the original classifier relied on prompt instruction alone ("return ONLY valid JSON") with a regex-stripped parse as a safety net. Gemini's responseSchema constrains the output at the decoding level, not just by instruction — the model literally cannot emit a field outside the declared enum. The classification taxonomy below is unchanged; only the enforcement mechanism is stronger.
// gemini/classify.ts · Cloud Run · gemini-3.1-flash-lite · temp=0.0

const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });  // ← Secret Manager, never client code

// Classification taxonomy — unchanged from the design phase:
// intent:      "cross-system" | "live-status" | "inventory" | "pipeline" | "single-connector"
// connectors:  ["CRM"] | ["ERP"] | ["Ticketing"] | any combination
// format:      "TABLE" | "CHART" | "CARD" | "PROSE"
// sensitivity: "high" | "low"

const response = await ai.models.generateContent({
  model: 'gemini-3.1-flash-lite',
  contents: `${CLASSIFIER_GUIDANCE}\n\nQuery: "${query}"`,
  config: {
    temperature: 0,
    responseMimeType: 'application/json',
    responseSchema: {                      // ← decoding-level constraint, not just instruction
      type: Type.OBJECT,
      properties: {
        connectors:  { type: Type.ARRAY, items: { type: Type.STRING, enum: ['CRM','ERP','Ticketing'] } },
        format:      { type: Type.STRING, enum: ['TABLE','CHART','CARD','PROSE'] },
        sensitivity: { type: Type.STRING, enum: ['high','low'] },
      },
      required: ['connectors', 'format', 'sensitivity'],
    },
  },
});

// Confidence and format-confidence fallback rules (§7.2, F2/F3) are unchanged —
// they still govern what happens when the classifier is uncertain, not whether it is.

7.2 — Classifier failure taxonomy

Failure caseDetectionHandling path
F1 · Malformed JSON response JSON.parse() throws Retry once at temp=0.0. If retry fails, default to full connector fan-out (all three) and PROSE format. Log miss.
F2 · Confidence below 0.60 result.confidence < 0.60 Activate all three connectors. Conservative fallback — retrieval overhead is preferable to a silent routing miss. Log confidence value.
F3 · Format signal absent or low confidence result.format_confidence < 0.70 or field absent Apply deterministic rule: multi-connector → TABLE; single-connector scalar → PROSE. No additional LLM call required.
F4 · Connector set returned empty result.connectors.length === 0 Surface explicit "cannot determine routing" error to UI. Do not produce a synthesis. Log the query for review.
F5 · Connector timeout (>3s) Promise.allSettled() timeout Proceed with partial results from connectors that responded. Surface timeout warning per connector in source attribution. Log SLA miss.

7.3 — Rule-based query planner

The Query Planner takes the classifier output and generates per-connector sub-queries using a deterministic template, not a second LLM call. This removes ~180ms from the critical path and makes the planner fully unit-testable.

Figure 9 — Query planner · classifier output → per-connector sub-query template
CLASSIFIER OUTPUT {intent, connectors, confidence, sensitivity, format, signals} RULE-BASED PLANNER deterministic · no LLM call template[intent][connector] → {fields, filters, limit} saves ~180ms vs LLM planner fully unit-testable CRM sub-query {fields:["deal_stage","risk_flag","owner"], filter:{risk:true}} ERP sub-query {fields:["sku","quantity","fulfilment_status"], filter:{open:true}} Ticketing sub-query {fields:["ticket_id","priority","sla_status"], filter:{priority:"P1"}}
§08 Scaling Envelope

Architecture ceiling and three-stage graduation path

Resolution · H5 — Scaling envelope
The current architecture is a deliberate single-session design. This section defines where it breaks and the graduation path to team-scale and production.

Cloud Run's scale-to-zero, Firestore's Always Free daily quota, and the Gemini Developer API's free/low-cost tier constitute a single-tenant, budget-capped architecture. At demo scale every component sits inside a perpetual free quota; the binding constraint is not user count but the deliberately tiny $0.01 hard cap on the Gemini billing account, which is a policy choice, not a technical ceiling. The following table defines what changes at each tier and, critically, which change is a technical requirement versus a budget policy that could be lifted without any architectural change.

TierConcurrent usersBottleneckCacheLLM inferenceConnector layer
Demo
current
1 (allowlisted) $0.01 Gemini budget cap — policy, not quota Firestore Native mode, Always Free daily quota Gemini Developer API, AI Studio key Stateful simulators
Team-scale
Stage 2
10–50 Firestore Always Free read/write quota; Cloud Run concurrency Firestore, raised budget alert instead of hard cap Gemini Developer API, raised budget Simulators + 1 live connector (Salesforce Dev Ed.)
Production
Stage 3
500+ Connector connection pooling; LLM token throughput Memorystore (Redis) — no free tier, billed from first byte Vertex AI Gemini — SLA, quota management, VPC-SC Live Salesforce / SAP / Jira via Apigee + OAuth
Figure 10 — Scaling graduation path · component substitution by tier
STAGE 1 · DEMO STAGE 2 · TEAM STAGE 3 · PRODUCTION Firestore Native mode Gemini AI Studio · $0.01 cap Cloud Run + API Gateway Stateful simulators Firebase Hosting (Spark) $0 / month, Blaze attached Firestore, higher quota Gemini · raised budget alert Same Gateway, wider allowlist Simulators + 1 live connector Firebase Hosting (Blaze) ~$5–20 / month Memorystore (Redis) Vertex AI · Gemini, SLA-backed Cloud Run, higher concurrency Live Salesforce/SAP/Jira GKE or Cloud Run Variable · GCP pricing
§09 TOGAF Alignment

ADM Phase B + Phase C positioning

Resolution · M3 — TOGAF ADM framing
TOGAF references are now substantive. This document is positioned explicitly within TOGAF ADM Phase B (Business Architecture) and Phase C (Information Systems Architecture), with Architecture Building Blocks identified and the Architecture Vision (Phase A) mapped to the motivation section.

This document operates as a Phase B + Phase C artifact within the TOGAF Architecture Development Method. Phase B establishes the business context — the federation gap, the query taxonomy, and the stakeholder concern (Sales Directors, Ops leads) whose cross-system queries this system addresses. Phase C defines the information systems architecture — the connector federation model, the data flow between layers, and the freshness policy as a data architecture constraint.

9.1 — Architecture Building Blocks

ABBDescriptionRealised by (SBB)
Connector Federation ABBThe capability to dispatch parallel sub-queries to heterogeneous operational data sources via a standard interfaceLayer 4 · CRM/ERP/Ticketing connectors with query(intent, filters) interface
Query Intelligence ABBThe capability to classify natural language query intent and generate per-connector sub-queriesLayer 3 · Gemini 3.1 Flash-Lite classifier (AI Studio) + rule-based planner
Credential Isolation ABBThe capability to mediate all upstream API calls without exposing secrets to the clientLayer 2 · Cloud Run + API Gateway + Firebase Authentication
Freshness Policy ABBThe capability to enforce per-query-type TTL policies and surface data age to the userLayer 5 · Firestore TTL cache with consistency window check

9.2 — Phase A (Architecture Vision) mapping

The Architecture Vision is captured in §01 (Motivation): enterprise operational teams are blocked from answering cross-system queries because no single system of record contains the required join, and no existing tooling automates the federation. FederaQ addresses this gap without requiring application migration, new enterprise software licenses, or changes to existing systems of record — consistent with the TOGAF ADM principle of maximising reuse of existing infrastructure.[10]

§10 Architecture Decision Records

All significant architectural choices · traceable rationale

ADR-001 FederaQ positioned as Federated Query Synthesis, not RAG Accepted
Context
The original system was titled a "RAG system." RAG requires vector retrieval from an indexed corpus. FederaQ performs live API federation against operational systems of record. The distinction is architecturally load-bearing: freshness is a function of source system state, not index staleness.
Decision
Classify FederaQ as a federated query synthesis engine. The closest published patterns are Toolformer [3] for LLM-directed API invocation, Trino/Presto [11] for federated query execution, and DAIL-SQL for structured sub-query generation. All RAG references are removed except where document RAG is being contrasted as the inadequate prior approach.
Consequences
The system no longer claims RAG capabilities it does not implement. The architecture is correctly classified and comparable to published prior work. Any future extension to include vector retrieval over static documents would require a new connector type and a revised problem statement.
Alternatives
Retain "RAG" label with a redefined definition — rejected as misleading to any evaluator familiar with the retrieval-augmented generation literature.
ADR-002 Cloudflare Worker as credential isolation proxy — superseded by ADR-009 Superseded
Context
The original ADR-002 accepted the Groq API key in browser client code as "acceptable for a portfolio demo." This was an acknowledged vulnerability with no resolution path — not an architecture decision. A TOGAF-compliant security architecture must address credential isolation regardless of demo scope.
Decision
Introduce a Cloudflare Worker as a mandatory proxy layer between the browser client and all upstream API calls. The API key is stored as a Cloudflare Worker environment secret (via Wrangler), never deployed to client code. The Worker enforces CORS, validates request origin, and applies rate limiting via Cloudflare KV. The browser client calls the Worker endpoint only.
Consequences
API key is never exposed. Cold-start latency is zero (Cloudflare Workers use V8 isolates, not containers). Free tier covers 100,000 requests/day — sufficient for demo and team-scale use. CORS surface is controlled at the Worker, not the LLM provider.
Alternatives
FastAPI proxy on Railway free tier — adds 500ms–2s cold-start latency and operational complexity. Rejected. Netlify Functions — viable alternative but requires a build step. Cloudflare Workers preferred for zero cold-start and edge deployment.
Superseded by
ADR-009. This decision is retained verbatim as the historical record — it was the correct call for a non-GCP-hosted design. The GCP-native migration replaces the platform, not the reasoning: both decisions solve the same problem the same way in spirit — mandatory server-side mediation, zero client-held secrets.
ADR-003 Stateful mutable simulators over static mock connectors Accepted
Context
The original design used static JSON objects as mock connectors. Static connectors cannot exercise the TTL cache layer — no state change means no TTL miss, no cache invalidation event, and no demonstration that the freshness policy produces different results on a repeated query. The freshness policy section was therefore unvalidated design.
Decision
Replace static JSON mocks with stateful mutable simulators. Each connector seeds its initial state at page load and applies a deterministic delta function on a fixed interval (CRM: 20s, ERP: 15s, Ticketing: 30s). All intervals are shorter than the corresponding TTL values, guaranteeing real TTL miss events during a demo session. The connector interface is unchanged: query(intent, filters) → {data, source, fetched_at, ttl_policy}.
Consequences
The freshness policy is exercised under real conditions. The simulator can demonstrate a TTL miss producing a changed value. The connector interface remains real-API-swappable. State is not persistent across page reloads — acceptable for demo scope.
Alternatives
Salesforce Developer Edition for CRM — free but covers only one connector. ERP and Ticketing have no equivalent free sandbox. Deferred to Stage 2 scaling.
ADR-004 Rule-based Query Planner over second LLM call Accepted
Context
The Query Planner generates per-connector sub-queries from classifier output. Two implementation options exist: a second LLM call (a Groq call in the original design, a Gemini call in the GCP-native rebuild) or a rule-based template engine over the classifier's structured output.
Decision
Implement the Query Planner as a deterministic rule-based template. Given classifier output {intent, connectors, signals}, a static template map generates the per-connector sub-query payload: template[intent][connector] → {fields, filters, limit}. This removes ~180ms from the critical path (one fewer model call, regardless of provider), makes the planner fully unit-testable, and eliminates a second point of LLM non-determinism.
Consequences
The planner handles only the query types defined in the template map. Novel query types not in the map require a template addition. For the current four-connector, five-intent taxonomy, this is a manageable surface. A second LLM call would be required if query decomposition complexity grows beyond what templates can express.
Alternatives
Second Groq LLM call — adds ~180ms latency, introduces non-determinism, requires prompt maintenance. Rejected for the current query taxonomy.
ADR-005 Adaptive output format selection with confidence threshold fallback Accepted
Context
Format selection must handle the case where the LLM format signal is absent or low-confidence. The original design fell back to PROSE for all unresolved cases. A ranked table of 40 deals rendered as prose is operationally unusable — the document itself acknowledged this while retaining the contradiction.
Decision
Introduce a format confidence threshold (0.70). If format_confidence ≥ 0.70, use the LLM-supplied format signal. If below threshold or absent, apply a deterministic rule: multi-connector queries default to TABLE; single-connector scalar queries default to PROSE. This is consistent with the primary use case and requires no additional LLM call.
Consequences
Format fallback is now consistent with usability requirements for cross-system queries. The deterministic rule is auditable and testable. Edge cases remain possible where the deterministic rule selects a suboptimal format, but these are preferably wrong rather than unusably wrong.
Alternatives
Always PROSE — rejected; unusable for cross-system results. Always TABLE — rejected; produces poor output for single-scalar queries and prose explanations.
ADR-006 Firestore TTL cache with consistency window check — revised Revised
Context
Cache layer is needed to demonstrate the freshness policy without hitting connectors on every query. The original design used a browser-side in-memory Map with TTL — which cannot survive the move to a server-side Cloud Run proxy, since Cloud Run instances are stateless and scale to zero between requests; an in-memory cache would evaporate on every cold start. A real persistent store is now a hard requirement, not an optimisation.
Decision
Use Firestore Native mode (europe-west2) as the TTL cache, keyed by a hash of the query text, with expiry checked in application code at read time rather than relying on Firestore's own TTL policy deletion (which runs on a delayed background schedule, not query-time). Memorystore — Google's managed Redis-compatible service — was the more obvious fit for a TTL cache, but it has no free tier at all; it is billed from the first byte, which conflicts directly with the $0-target constraint. Firestore's Always Free daily quota (50,000 reads / 20,000 writes / 20,000 deletes) covers demo-scale traffic at $0. The consistency window check is unchanged: compute max(fetched_at) - min(fetched_at) across all connector results in a synthesis call; if delta > 120s, append a staleness warning to the source attribution panel.
Consequences
No automatic expiry enforcement (checked at read time, not Firestore-enforced) and no distributed invalidation across concurrent Cloud Run instances — acceptable at single-service, allowlisted-user scale. High-sensitivity query types still bypass the cache entirely regardless of backing store. Memorystore remains the documented production upgrade path once traffic or invalidation complexity exceeds what a simple read-time TTL check handles well (§08, Stage 3).
Alternatives
Memorystore (Redis) — rejected for demo scope, no free tier. Cloud Run in-memory cache — rejected, does not survive scale-to-zero cold starts. Cloudflare KV — no longer applicable once the proxy platform moved to GCP (ADR-009).
ADR-007 Static single-file HTML delivery Accepted
Context
Portfolio artifact must be hostable on GitHub Pages with zero build infrastructure and zero cost.
Decision
Single index.html — all CSS, JS, SVG diagrams, simulator logic, and ADRs self-contained. No bundler, no framework, no build step.
Consequences
File grows large. No component separation. Acceptable for a portfolio artifact — prioritises deliverability over maintainability. Simulator interactivity achieved via vanilla JS without framework overhead.
Alternatives
React + Vite on Vercel — unnecessary complexity for a static portfolio document.
ADR-008 Freshness policy as a query-type routing layer with derived TTL values Accepted
Context
Not all enterprise queries require live data. The original TTL values (inventory 60s, tickets 90s, deal stage 5min, account info 30min) were asserted without derivation. This revision provides a documented derivation basis for each value.
Decision
Retain the TTL policy map. Add derivation rationale per query type (see §05.1). Values are design-phase estimates; production values would be empirically calibrated against actual source-system event log update frequencies. High-sensitivity types (inventory, live tickets) bypass cache regardless of TTL status.
Consequences
Staleness risk on low-sensitivity queries remains. Policy map must be maintained as query types evolve. Data age is surfaced to UI on every response, mitigating user trust risk. Consistency window check (ADR-006) provides an additional signal for temporally skewed results.
Alternatives
Always-live (no cache) — correct but expensive and high-latency at scale. Always-cached — cheap but operationally unacceptable for freshness-sensitive queries.
ADR-009 Cloud Run + Google API Gateway + Firebase Authentication as GCP-native credential isolation Accepted
Context
The GCP-native rebuild retires the Cloudflare Worker (ADR-002) — not because it was wrong, but because the target platform changed. The available GCP billing account has no balance; every resource choice has to sit inside an Always Free perpetual quota, and the account must not be reachable by an anonymous caller who could run up the connected Gemini billing account.
Decision
Front Cloud Run with Google API Gateway. Firebase Authentication (Google Sign-In only) issues an ID token; API Gateway validates that token against Firebase's issuer (securetoken.google.com/<project>) before a request is ever forwarded, and passes the decoded claims to Cloud Run via the x-apigateway-api-userinfo header. Cloud Run grants roles/run.invoker only to the Gateway's own service account — never allUsers — so Cloud Run is not directly reachable from the internet at all. Cloud Run additionally checks the forwarded email against an allowlist before doing any Gemini call.
Consequences
Two authorisation checks instead of one (Gateway's JWT validation, then Cloud Run's allowlist) — more moving infrastructure than a single Cloud Run service, but it is the least-privilege pattern: no caller reaches billable compute without a verified Google identity, and no caller reaches the Gemini key without also being on the allowlist. All four pieces (Cloud Run, API Gateway, Firebase Auth, Firestore) require a Blaze billing account attached to the project even though usage is designed to stay inside Always Free quotas — that requirement is unavoidable on GCP and is addressed directly as a budget-governance decision in ADR-011, not worked around here.
Alternatives
Cloud Run with no Gateway, validating the Firebase JWT in application code directly — rejected; it works, but puts token-validation logic in the same codebase as business logic instead of a declarative, auditable OpenAPI security definition. Firebase Hosting + Cloud Functions (Spark plan) — rejected; Spark-tier Cloud Functions cannot make outbound calls to non-Google APIs reliably enough for a proxy that must reach the Gemini Developer API, and Blaze is required for Cloud Functions regardless once real usage is expected.
ADR-010 Gemini two-tier inference via the AI Studio Developer API Accepted
Context
Groq is not a GCP-native service. Moving the proxy to Cloud Run is the natural point to also move inference to a Google-native LLM. Classification and synthesis have different cost/quality requirements — classification runs on every query and needs to stay cheap; synthesis is where reasoning quality is user-visible.
Decision
Route classification to gemini-3.1-flash-lite and synthesis to gemini-3.5-flash, both called from Cloud Run via the @google/genai SDK against the AI Studio Developer API (an API key, not Vertex AI's IAM-based auth). The key lives in Secret Manager, injected into Cloud Run as an env var at deploy time. Gemini's native responseSchema replaces the prompt-instruction JSON parsing the Groq classifier relied on (§7.1) — the model is constrained at the decoding level rather than by instruction alone.
Consequences
Two model calls to reason about instead of one, but each stage runs the right-sized model for its job, matching the original Groq design's single-model-two-jobs tradeoff analysis, just with a stronger structured-output guarantee. Vertex AI remains the documented production upgrade (§08, Stage 3) for SLA guarantees, quota management, and regional data residency — deliberately not adopted now, since Vertex AI's IAM-based billing model doesn't map cleanly onto the AI Studio billing account this build is scoped to use.
Alternatives
Vertex AI Gemini now instead of at production graduation — rejected for this phase; it requires project-level IAM setup disproportionate to a $0.01-capped demo, and the user's existing billing relationship is with AI Studio specifically. Keeping Groq behind the new Cloud Run proxy — rejected; defeats the purpose of a GCP-native rebuild and adds a second external vendor dependency for no benefit.
ADR-011 Hard-capped, auto-disabling budget on the shared AI Studio billing account Accepted
Context
The Gemini API key used by this project bills against an AI Studio billing account that is shared with other, unrelated projects and carries a fixed $10 balance. A Cloud Billing Budget alert on its own only sends a notification — it does not stop spend — and $0.01 leaves no margin for a stray retry loop or an unexpectedly large classification/synthesis batch to eat into balance meant for other work.
Decision
Scope a Cloud Billing Budget to just this project's Generative Language API SKU, set at $0.01, wired to a Pub/Sub notification that triggers a Cloud Function to disable the Gemini API key immediately on breach — not merely email a warning. This is layered on top of, not instead of, keeping demo traffic on Gemini's free-tier rate limits wherever the model choice allows it, so the paid path is a safety margin rather than the expected mode of operation.
Consequences
A false-positive disable (e.g. a legitimate short burst of demo traffic) locks out the key until manually re-enabled — an intentional tradeoff, since the alternative is risking spend against a balance shared with other projects. This budget is separate from, and does not affect, the main GCP project's own $0 budget alert on Cloud Run/Firestore/API Gateway usage (§08), which sits on different billing infrastructure entirely.
Alternatives
Budget alert only, no auto-disable action — rejected; an alert that arrives after the spend has already happened doesn't cap anything. Separate, dedicated billing account for this project alone — preferable in principle, but not available given the user's current $10 AI Studio account is the one with an active balance.
§11 Simulator

Architectural parameter simulator · TTL and latency instrumentation

Resolution · M2 — Simulator as architectural tool
The simulator now exposes two architectural parameters as interactive controls: connector latency (showing how P50 synthesis latency changes) and TTL expiry (demonstrating cache hit vs. cache miss behavior on repeated queries). It is an instrument that reveals architectural tradeoffs, not a product demo.
federaq · pipeline simulator · design phase
Architectural Parameters
Connector latency 600ms
Gemini inference latency 300ms
Simulate TTL expiry (connector state mutates between runs)
Estimated P50: ~1.2s · P99 (2s connector): ~3.5s
"Which at-risk deals this quarter have fulfilment issues that could affect close?"
CRM · Salesforce
idle
ERP · SAP
idle
Ticketing · Jira
idle
01Receive
NL query
02Classify
intent
03Plan
sub-queries
04–06Connector
fetch
07Freshness
check
08Synthesise
Gemini
09Render
output
Select a scenario and run simulation
Design phase · simulator caveat
This simulator uses pre-scripted scenario responses with stateful connector mutation. Connector state drifts on a seeded schedule — run the same scenario twice with TTL expiry enabled to observe a cache miss returning a changed value. In the build phase, the Gemini intent classifier and synthesiser — called from the Cloud Run proxy described in §04, behind Firebase Sign-In — replace the scripted responses, enabling arbitrary natural language queries. Production deployment replaces mock connectors with live Salesforce, SAP, and Jira integrations.
§12 References

Citations

  1. [1]Thakur, N. et al. (2021). BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models. NeurIPS 2021 Datasets and Benchmarks Track. arXiv:2104.08663.
  2. [2]Dresner Advisory Services. (2024). Wisdom of Crowds® Business Intelligence Market Study. Dresner Advisory Services LLC. [Analyst report; context-switching productivity data cited from executive survey findings.]
  3. [3]Schick, T. et al. (2023). Toolformer: Language Models Can Teach Themselves to Use Tools. NeurIPS 2023. arXiv:2302.04761.
  4. [4]Google LLC. (2026). Gemini API Documentation — Models, Rate Limits, and AI Studio Developer API. ai.google.dev/gemini-api/docs. [Gemini 3.1 Flash-Lite and Gemini 3.5 Flash; Developer API key auth via AI Studio; free-tier and pay-as-you-go pricing.]
  5. [5]Guu, K. et al. (2020). REALM: Retrieval-Augmented Language Model Pre-Training. ICML 2020. arXiv:2002.08909.
  6. [6]Google LLC. (2026). Cloud Run, API Gateway, and Firebase Authentication Documentation — Always Free Tier Limits and Scale-to-Zero Behaviour. cloud.google.com/run/docs, cloud.google.com/api-gateway/docs, firebase.google.com/docs/auth. [Cloud Run Always Free: 2M requests/month; scale-to-zero with cold start on first request; API Gateway JWT validation against Firebase issuer.]
  7. [7]SAP SE. (2023). SAP Materials Management (MM) Configuration Guide — Batch Job Scheduling for Inventory Posting. help.sap.com. [Standard batch posting cycle documented at 60–300s for SMB deployments.]
  8. [8]Atlassian Inc. (2024). Jira Cloud REST API Documentation — Webhooks: Delivery SLA and Retry Policy. developer.atlassian.com/cloud/jira. [Webhook delivery latency documented up to 60s; retry on failure.]
  9. [9]Salesforce Inc. (2024). Salesforce Streaming API Developer Guide — Push Topics and Event Delivery Latency. developer.salesforce.com. [CRM Streaming API push latency 1–3s; deal stage human-update frequency empirically measured in minutes to hours.]
  10. [10]The Open Group. (2018). TOGAF® Standard, Version 9.2. The Open Group. ISBN: 978-94-018-0-303-6. [ADM principles: maximise reuse, Phase B Business Architecture, Phase C Information Systems Architecture.]
  11. [11]Sethi, R. et al. (2019). Presto: SQL on Everything. IEEE ICDE 2019. [Federated query execution model; connector interface design principles.]
FederaQ · Architecture Design Document · rev 1.0 · 2026 Siddharth Rao · TOGAF EA · GCP CA · MLE · Gen AI Leader Design phase — build phase follows with Gemini integration (Cloud Run + API Gateway + Firebase Auth) and live connector wiring