# Enterprise GenAI Architecture: Define Boundaries and Failure Budgets

> Design a GenAI copilot with explicit trust boundaries, data-flow checks and measurable failure budgets, then run a TypeScript policy example.

- Drafted by AI, signed off by: [Muhammed Shakir](https://www.linkedin.com/in/muhammed-shakir-gen-ai-trainings-and-consulting): Creator, Projekts.ai | Enterprise AI Builder & Architect | Helping Businesses Build AI-Agent-Driven Operations | Agentic AI, RAG & AI-Native Systems | Forward Deployed Engineer | Corporate Trainer
- Published: 2026-11-01
- Last updated: 2026-11-01
- Topic: Agent tools and architecture
- Written for: architects and developers
- Web page: https://workshopps.com/blog/enterprise-genai-architecture-boundaries-and-budgets
- Related Course Offering: [GenAI Design & Architecture — Engineering Robust Enterprise AI Systems](https://workshopps.com/courses/genai-design-and-architecture)

**At a glance:** Define an enterprise GenAI architecture around the user task, permitted data flows and enforced trust boundaries. Set separate measures for service reliability, answer quality and security, then assign owners and failure responses. A worked copilot design and runnable policy example show how to make those decisions before selecting models or frameworks.

## The short answer

Define an enterprise GenAI architecture by writing down the user task, the data each component may handle, and the checks required whenever that data crosses a boundary. Then set separate acceptance measures for service reliability, answer quality and security, with an owner and a response when each measure fails. A model choice comes after these decisions.

Consider a fictional engineering knowledge assistant. An engineer asks which deployment checks apply to a service, and the assistant answers from documents that engineer is allowed to read. We will turn that modest task into a reviewable architecture and run a small policy example without calling a model.

---

## Start with a task and an explicit limit

Write the first version of the system contract before drawing components:

| Design question | Decision for this teaching example |
| --- | --- |
| Who uses it? | Authenticated engineers within one organisation |
| What can it do? | Explain deployment guidance using authorised documents and source links |
| What must it decline? | Questions without sufficient permitted evidence |
| What can it change? | Nothing: no deployment, ticket update or configuration write |
| Who owns an incorrect answer? | The application owner investigates; the document owner corrects source guidance |

This is the system boundary: the responsibility the application accepts. A request to deploy a service is outside it, even if the model can produce convincing deployment commands.

Do not use this answer-generation design when the user only needs an exact document lookup or a deterministic status value. Search or an ordinary service API may satisfy that task with fewer things to evaluate. Add generation when explaining and combining authorised evidence creates a useful result.

---

## Put checks on the arrows

A trust boundary marks a crossing where the next component must establish what it may accept or do. A trust zone groups components with similar assumptions and controls; putting two boxes in one zone does not make every request between them safe.

[NIST's zero-trust architecture guidance](https://www.nist.gov/publications/zero-trust-architecture) bases access on resource checks rather than assuming that an internal network establishes trust. Apply that principle to the copilot's data flows:

| Crossing | Enforced decision | Evidence for the review |
| --- | --- | --- |
| Browser → application API | Verify identity and derive organisation and permissions on the server | An altered organisation field cannot widen access |
| Application → knowledge store | Retrieve within the caller's authorised scope | A forbidden document never enters model context |
| Application → model provider | Check the destination is approved for every field being sent | A restricted excerpt blocks the outgoing request |
| Application → telemetry store | Emit a selected set of operational fields | Routine events contain no raw questions or document text |
| Generated answer → browser | Validate structure, source identifiers and safe rendering | An invented source identifier is rejected |

![Four stages show verified caller scope, scoped evidence, a destination policy check and an approved model provider. A separate telemetry path carries selected metadata.](https://infomover-public-content.s3.ap-south-1.amazonaws.com/prod/workshopps/enterprise-genai-architecture-boundaries-and-budgets/boundary-decisions.svg "Check access and destination policy before evidence leaves the application.")

The diagram's key decision is before the provider call. Removing private text from the final answer is too late to prevent sending it to the provider.

Authorised content can still contain malicious instructions. [OWASP's prompt-injection guidance](https://genai.owasp.org/llmrisk/llm01-prompt-injection/) describes how external material can redirect model behaviour and recommends constrained privileges and controls outside the model. In this design, retrieved text supplies evidence; it cannot grant access or introduce a deployment tool. Keeping the assistant read-only limits possible actions, but it does not guarantee a correct or confidential answer.

---

## Classify the flow, including the question

Classification describes how data must be handled. Authorisation determines whether a particular caller may access it. Passing one check does not pass the other: an engineer may read a restricted design document while the application's external model endpoint is prohibited from receiving it.

For this fictional exercise, define three labels and document their handling:

| Label | Example | Model destination rule |
| --- | --- | --- |
| Public | Published deployment guide | Approved public-data endpoint |
| Internal | General engineering runbook | Endpoint explicitly approved for internal data |
| Restricted | Security-sensitive system design | No model call in this version |

These labels and rules are examples, not a universal classification scheme. Use your organisation's actual policy, including provider processing, retention, location and contractual requirements.

Inventory the complete outgoing payload: the user's question, conversation history, retrieved excerpts and any attachments. A public document does not make a question containing a secret public. Apply checks before embedding or indexing sensitive material too; ingestion and retrieval can have different destinations.

For routine telemetry in this exercise, retain a request identifier, policy outcome, elapsed time and component version. Treat even those fields as data requiring access and retention decisions. If an investigation needs content, define a separate controlled capture process rather than quietly enabling full payload logging.

---

## Run a boundary decision before adding a model

The following TypeScript example models two checks: whether a caller may read an excerpt and whether an approved destination may receive its classification. It rejects the entire batch if any excerpt fails, so it cannot silently generate an answer from a partially removed evidence set.

Use Node.js 22 or newer with `tsx`. In a fresh scratch directory, run `npm init -y` and `npm install --save-dev tsx`, save this as `boundary.ts`, then run `npx tsx boundary.ts`. The [Node.js TypeScript documentation](https://nodejs.org/api/typescript.html) describes this execution approach. There are no model credentials or network calls in the example itself.

```typescript
import assert from "node:assert/strict";

type Classification = "public" | "internal" | "restricted";
type Principal = { organisation: string; readableIds: Set<string> };
type Excerpt = {
  id: string;
  organisation: string;
  classification: string; // Runtime metadata can be invalid.
  text: string;
};

// Server-owned policy; never accept this set from the browser or model.
const destinationAllows = new Set<Classification>(["public", "internal"]);

function maySend(label: string): boolean {
  switch (label) {
    case "public":
    case "internal":
    case "restricted":
      return destinationAllows.has(label);
    default:
      return false; // Unknown labels fail closed.
  }
}

function checkedEvidence(principal: Principal, excerpts: Excerpt[]): string[] {
  for (const excerpt of excerpts) {
    if (excerpt.organisation !== principal.organisation ||
        !principal.readableIds.has(excerpt.id)) {
      throw new Error("read denied");
    }
    if (!maySend(excerpt.classification)) {
      throw new Error("destination denied");
    }
  }
  return excerpts.map(excerpt => excerpt.text);
}

const engineer: Principal = {
  organisation: "example-org",
  readableIds: new Set(["guide"]),
};
const guide: Excerpt = {
  id: "guide", organisation: "example-org",
  classification: "internal", text: "Check the rollback plan.",
};

assert.deepEqual(checkedEvidence(engineer, [guide]), [guide.text]);
assert.throws(() => checkedEvidence(engineer, [
  { ...guide, organisation: "other-org" },
]), /read denied/);
assert.throws(() => checkedEvidence(engineer, [
  { ...guide, id: "private-guide" },
]), /read denied/);
assert.throws(() => checkedEvidence(engineer, [
  { ...guide, classification: "restricted" },
]), /destination denied/);
assert.throws(() => checkedEvidence(engineer, [
  guide, { ...guide, classification: "unknown" },
]), /destination denied/);
console.log("Allowed path and four denied paths passed.");
```

The expected output is `Allowed path and four denied paths passed.` An assertion failure means the exercise has not passed.

In a real application, identity and permission metadata come from authenticated server services. The store must enforce scoped retrieval before returning text; this final check provides another enforcement point. Keep the checked payload bound to the destination and policy version actually used for the call.

This function is an excerpt-policy demonstration, not a complete secure API. It does not authenticate a caller, validate arbitrary request objects, classify text, inspect the question, detect prompt injection, validate answers or call a provider. An empty evidence set also needs an explicit decline path. TypeScript types do not validate incoming JSON; production boundaries need runtime validation and complete-payload checks.

---

## Turn quality attributes into observable scenarios

“Fast, secure and accurate” cannot settle an architecture review. Write a condition, a measurable response, a measurement method and an owner.

The following are deliberately invented teaching targets. They are starting points for negotiation, not recommended production thresholds.

| Attribute | Example acceptance scenario | Owner and response |
| --- | --- | --- |
| Service reliability | Over a rolling 28 days, at least 99.9% of authenticated, in-scope requests complete without infrastructure error within 8 seconds | Service owner investigates budget consumption and prioritises reliability work |
| Answer quality | Before release, at least 95 of 100 versioned evaluation cases satisfy every required check for supported claims, useful source links and the expected answer or decline | Evaluation owner blocks release and inspects failed cases |
| Confidentiality | Cross-organisation evidence and prohibited classifications fail every boundary test; any confirmed production disclosure is an incident | Security owner contains the affected path and investigates |
| Resource use | Each request has an enforced model-call and output-size limit set for its task | Application owner declines or degrades when the limit is reached |

For the service measure, count at the application boundary, not per provider attempt. Retries remain part of one user request. Define eligibility before observing outcomes: permitted declines count as completed responses, while timeouts and infrastructure errors count as failures. Never remove a request from the denominator because it timed out or exceeded 8 seconds; those are the failures the budget counts.

A permitted decline still may be the wrong answer to a well-supported question. That is why the evaluation measure is separate. Its 100 cases are a curated release check, not proof that 95% of future production answers will be correct. Review sampled production answers as a further signal, with appropriate data controls.

---

## Give the failure budget a denominator and a consequence

For a request-based service-level objective, the error budget is the allowed fraction of unsuccessful requests over the stated window. [Google SRE's example policy](https://sre.google/workbook/error-budget-policy/) connects that allowance to decisions about reliability work and releases.

Suppose our teaching service receives 200,000 eligible requests in 28 days. At 99.9%, the allowed unsuccessful fraction is 0.1%: a budget of 200 requests. If 140 have failed the service measure, 60 remain at that observed volume.

```typescript
const eligibleRequests = 200_000;
const targetBasisPoints = 9_990; // 99.90% of 10,000 basis points
const allowedFailures = Math.floor(
  eligibleRequests * (10_000 - targetBasisPoints) / 10_000
);
const observedFailures = 140;
console.log({ allowedFailures, remaining: allowedFailures - observedFailures });
// { allowedFailures: 200, remaining: 60 }
```

This is arithmetic for a snapshot of a rolling window. Request counts and failures change as events enter and leave that window. It does not estimate how long the remaining budget will last.

Agree on the consequence before a breach. For this exercise, exhausted service budget stops feature releases while the service owner addresses reliability; an answer-quality regression blocks the affected model or prompt release. A confirmed data disclosure triggers containment immediately, regardless of how much service budget remains. There is no allowance to spend on unauthorised disclosure.

---

## What this means for you

An architect should leave the review with decisions that can be challenged and tested. A developer should be able to identify the code that enforces each decision.

Try this exercise with an anonymised use case:

1. Write one allowed task, one expected decline and one excluded action.
2. Draw the complete path, including ingestion, model processing and telemetry destinations.
3. Label each flow with classification, caller scope, enforcement point and owner.
4. Define one reliability objective, one answer-quality release check and one security stop condition.
5. Demonstrate a permitted request and a denied crossing; record which part is still an assumption.

For example, “the document's classification is always present” is an assumption until the missing-label path is tested. “The provider is approved” is incomplete until its permitted data classes and processing conditions are recorded. These gaps are useful review findings: they tell the team what to resolve before connecting more components.

---

## Questions architects ask

### Do trust zones replace permission checks?

No. A zone communicates design assumptions. Each resource boundary still needs enforced access decisions, including calls made by application services on a user's behalf.

### Should we switch models when the service budget runs out?

Only if the alternative has already passed the relevant destination-policy and answer-quality checks. A faster fallback can change both data handling and response behaviour. Make fallback an evaluated architecture decision rather than an emergency assumption.

### Where can a team practise these decisions together?

Day 1 of [GenAI Design & Architecture](/courses/genai-design-and-architecture) develops the enterprise copilot's boundaries, data domains and quality attributes before comparing retrieval approaches. Teams can use that shared case to defend their choices and identify the evidence an architecture review needs. **Enquire** about the Course Offering for your team.
