Chapter 53 Secrets, Permissions, Tenant Isolation, and Supply Chain Security
Mira asks for order A-104. The assistant returns “shipped,” a perfectly plausible status—but it belongs to Leo in another tenant. Nothing in the sentence looks like a jailbreak, and a content-safety filter may find no problem. The defect is that the query selected the wrong person’s record.
A second failure can look equally ordinary: a useful Skill changes by one line and starts reading a secret file. The tool name is still allowed. These failures ask questions that answer quality cannot settle: whose authority is in use, which objects it covers, and which exact code or asset is being trusted? We will follow those questions through the same local assistant, completing KA-8 without adding a multi-agent platform.
53.1 Identity comes from the host, not the proposed arguments
Section titled “53.1 Identity comes from the host, not the proposed arguments”A tenant is a separately administered group whose data must remain within its access boundary. Our new, versioned fixture has two tenants. North contains Mira’s A-104, status processing; south contains Leo’s A-104, status shipped. The repeated order ID is intentional. Mira and Leo are display names; their stored user IDs are mira and leo. Earlier read-only examples keep their original fixtures unchanged.
Example 53.1 — An ID is not a complete access decision. Mira’s trusted host context says tenant north, user Mira, audience orders-local, and scope orders:read:own. The allowed tool argument supplies only order_id: A-104. A correct lookup must match tenant, order ID and owner:
SELECT * FROM ordersWHERE tenant = ? AND id = ? AND owner = ?;The values are bound as parameters: north, A-104, mira. A parameter is a data value passed separately from the SQL structure; the model does not construct this query. The result is processing. The intentionally unsafe query instead accepts south from untrusted content and returns shipped. Adding an ownership check while still letting the model choose identity would not fix the underlying trust problem.
The guarded dispatcher rejects the unexpected tenant argument. Storage independently enforces the tenant and owner from the principal. Actual tests also change the host test principal to north/Leo or south/Mira; both fail because neither owns the selected row. A legitimate south/Leo principal gets shipped. Isolation means correct separation, not making south’s service unusable.
The simulator’s principal is a host-created identity record. Arbitrary Python with access to the process could construct one; this is not a real authentication service. In production, verifying a credential and establishing trusted request context must happen before these checks. A model saying “I am Mira” is no such verification.
53.2 Use credentials only where authority is exercised
Section titled “53.2 Use credentials only where authority is exercised”A secret is information whose disclosure would violate a boundary; a credential is evidence used to establish access authority. Some secrets, such as access tokens, are credentials. The fictional cabinet code from Chapter 52 demonstrates a disclosure rule but is not our application’s authentication mechanism.
Credentials should reach the component that needs them through a controlled host path. They should not be copied into model context, retrieved documents, Skill instructions or routine logs. A token’s audience identifies the service it is meant for. Its scopes describe permitted operations, and its expiry limits duration. Passing a token intended for one service to another does not make it valid there.
Table 53.1 — Permission matrix for the local assistant. These are enforced simulation rules, not real OAuth grants.
| Principal and request | Automatic operation? | Additional requirement | Actual behavior |
|---|---|---|---|
| Current north/Mira reader; read own A-104 | Yes | Correct audience, scope, tenant and owner | Returns processing |
| Reader; cancel own A-104 | No | Cancellation scope plus matching approval | Missing write scope rejects |
| Writer; cancel own A-104 | Only after approval | Approved exact action, subject, tenant, version and expiry | Valid approval produces one effect |
| Writer; missing, denied or expired approval | No | A valid approval is absent | Zero effects |
| North reviewer with approval role/scope | May create an approval record | Same tenant and explicit decision | Can approve or deny; cannot thereby read or cancel orders |
| Expired credential, wrong audience, missing read scope, or wrong owner | No | Establish valid authority first | Request rejected |
| Model supplies another tenant or arbitrary network tool | No | Those proposals are outside the interface | Schema or allowlist rejects |
Least privilege means giving a component only the authority it needs for its role. A read-only assistant does not need a cancellation credential. An approver needs authority to record a decision, not unrestricted order access. A source-verification helper needs its inputs and reviewed resources, not the user’s credential.
An approval is also bounded. Approval for north/Mira to cancel A-104 at version 1 does not approve A-105, south’s A-104 or a later changed version. Chapter 49’s action fingerprint binds the operation and arguments; the approval record also binds subject, tenant and expiry. The tests reject changed arguments, expired approvals and a writer attempting to act as approver. A human-looking sentence in a tool response cannot populate that record.
53.3 Check the bytes, then ask who vouched for them
Section titled “53.3 Check the bytes, then ask who vouched for them”Example 53.2 — A useful Skill changes after review. We review version 1.0.0 of the source-verification package and store its file paths and SHA-256 digests in an independently retained inventory. SHA-256 produces a fixed-length digest from file bytes. If the file changes, the comparison is expected to fail; this is an integrity check, not an explanation of whether the file is safe.
The regression copies the reviewed Skill, helper script and local MCP implementation into separate temporary files. Each first matches its recorded digest. Appending an unreviewed line makes each fail. The runner rejects the changed bytes before executing them. A symlink pointing outside the permitted root is rejected as well, so a seemingly harmless path cannot silently select an unreviewed target.
Do not calculate the “expected” digest from the offered file immediately before comparing it with itself. The expected inventory must be the previously reviewed snapshot. Our normal record runner loads that inventory and stops on mismatch; it does not refresh approvals automatically. An intentional update needs review, a new snapshot and rerun evidence.
Table 53.2 — Provenance questions differ across dependencies.
| Dependency | Evidence retained here | What remains unproved |
|---|---|---|
| Source Skill and its assets | Origin in this project, version, license, reviewed file digests, helper tests | A hash cannot prove the original author’s decisions were safe |
| MCP server implementation | Local source digest, read-only contract, protocol reference, existing tests | Protocol compatibility does not authenticate every server operator |
| Python packages | Exact installed versions and distribution metadata; inherited requirements lock | Pinning does not prove the registry, maintainer or installation artifact was uncompromised |
| Local model | Registry/tag, installed manifest digest, five verified content blobs, upstream license reference | No independently verified conversion/build attestation linking the quantization to upstream weights |
| Model runtime/service | Recorded runtime version, API settings and local CPU observations | No production service security assessment or provider-retention guarantee |
The installed Qwen manifest digest is 458ce03a21878025d072b8740d0e0e3b34abec3585fdadb0b5ab92d1e85dfb61. All five installed blobs match their manifest sizes and digests in the actual local inspection. A separate offered digest of 64 zeroes is rejected by comparison with the pinned value. We do not corrupt or execute modified weights, and no weight files are redistributed.
These checks establish local identity and consistency, with an explicit provenance gap. A malicious publisher could supply both malicious bytes and a matching digest. A compromised reviewer could approve the wrong inventory. Stronger build provenance needs trusted builder identity and verified expectations, as SLSA explains; this unsigned teaching inventory claims no SLSA level. A working Skill, MCP connection or model is evidence of availability, not evidence that its source is trustworthy.
53.4 Test confinement without confusing it with authorization
Section titled “53.4 Test confinement without confusing it with authorization”A sandbox restricts what a process can access or change. It operates at a different boundary from “may Mira cancel this order?” A process can be well confined and still misuse records it is allowed to access. Conversely, a correctly authorized operation can run in an unnecessarily powerful process.
Example 53.3 — A real, limited process experiment. In an optional macOS probe, create a public fixture file, a protected directory containing only DEMO-SECRET-ORCHID, and a loopback listener on the same computer. Run the same Python subprocess once normally and once under a confinement profile. No external destination is contacted.
Table 53.3 — Observed process behavior.
| Attempt | Without the profile | With the profile |
|---|---|---|
| Read permitted fixture | Reads public-fixture | Reads public-fixture |
| Read designated protected fixture | Reads the fictional secret | PermissionError, errno 1 |
| Connect to local listener | Connects | PermissionError, errno 1 |
This profile denies all network access, denies reads in the designated protected fixture directory, and restricts writes to the temporary work directory and /dev/null. General runtime reads remain allowed. It is a macOS-specific diagnostic profile, not a general filesystem allowlist or a production sandbox. An earlier profile prevented Python from starting; that failed attempt is retained separately instead of being counted as successful confinement. A process that cannot do its legitimate job is not a sufficient positive test.
The measured result establishes those specific file and network boundaries on the recorded platform. It does not prove resistance to escapes, protect every file on the host, or test another operating system. The portable CPU suite checks dispatch and authorization separately; it does not silently pretend to recreate this OS-level experiment.
- Application recordHost: north / mira
- An operationWHERE tenant=north AND owner=mira
- Outside the modelA-104: processing
The same ID in south belongs to leo and is shipped; an order ID alone is insufficient.
- Application recordReviewed source + expected digest
- An operationAllowlist + sandbox + approval
- Application recordScoped effect + audit receipt
An unchanged malicious package can pass a hash check. An isolated process can still misuse an authorized credential.
Figure 53.1 keeps the controls separate: host identity selects authority; approval permits a specific write; provenance selects reviewed bytes; confinement limits process reach; audit records preserve decisions and effects. Removing any one can leave a different path open. Audit does not prevent an unauthorized action by itself, but without it we may be unable to reconcile an uncertain result.
53.5 Complete the project by stating its acceptance boundary
Section titled “53.5 Complete the project by stating its acceptance boundary”The assistant now has a finite RAG workflow with one bounded model choice, a reusable source-verification Skill, durable simulated action recovery, layered evaluation and a governance/security record. Its original Keyword, BoW Count and TF-IDF comparisons remain intact. The prior Mini GPT decision remains intact too; using Qwen for separate local probes did not change that model’s acceptance status.
Table 53.4 — KA-5 through KA-8 handoff evidence.
| Milestone | Observable result | Residual responsibility |
|---|---|---|
| KA-5 | One allowed choice; current 750; empty evidence and exhausted budgets stop | More tasks need their own evidence and transition contracts |
| KA-6 | Skill trace; rejected approvals cause zero effects; lost-reply recovery leaves one effect; replay makes zero mutations | Remote services need their own idempotency/reconciliation contract |
| KA-7 | Two systems both 3/4 with different first failures; four travel human cases agree; public human slice agrees 0/4 | Automatic Judge release remains disabled; more independent domain review is needed |
| KA-8 | Minimized fields, four-store deletion, expiry, local attack blocking, tenant/approval tests, changed-byte rejection and bounded OS probe | Real identity, operational retention, backups, source rights, incident response and secure deployment remain human/system duties |
The project is a local teaching system, not an operational service. Its core notebooks run on CPU using small fixtures and saved optional-model records. Model calls, human judgments, hand-given examples and actual host execution remain distinguishable. Small security regressions can prevent known mistakes from returning; they cannot prove that no unknown attack exists.
53.6 Part checkpoint: explain the control without naming a framework
Section titled “53.6 Part checkpoint: explain the control without naming a framework”Try these six transfer questions before reading the answers below. They connect the entire part rather than require memorizing component names.
- Evidence is empty and a model asks to search forever. Which component stops the request, and which state should be retained?
- A new source-verification package has the same name but different bytes. What must happen before it can run?
- A cancellation reply is lost. Which evidence distinguishes “not done” from “done but not reported,” and why is a new key dangerous?
- Two systems each pass 3/4 requests. What additional record tells you where to repair them?
- A cited source is authentic but wrong. Who resolves the substantive dispute, and which current/historical checks should follow?
- A sandboxed tool receives a valid north credential and a model-supplied south tenant. Which controls still have to reject the request?
53.7 Summary
Section titled “53.7 Summary”Authority follows a verified principal and a scoped operation, not persuasive text or an order ID alone. Reviewed digests detect changes relative to a known inventory; they do not create trustworthy provenance. A sandbox limits reach but does not grant authorization. KA-8 combines these controls with audit and explicit remaining duties. Chapter 54 asks how to turn this local design into a usable service while preserving those boundaries.
53.8 Exercises
Section titled “53.8 Exercises”- North and south both have A-104. A cache uses only order ID as its key. Explain a possible leak and specify the additional identity and version information needed when using cached results.
- A credential permits reads and cancellation, but its audience is another service. Should an approved cancellation proceed? Identify the first failing condition.
- A reviewer approves version 1, but the order reaches version 2 before execution. What should happen, and why does “same order ID” not preserve approval?
- A changed helper comes with a new matching checksum from its own download page. Which integrity check can pass, and which trust question remains?
- The sandbox blocks the fake secret but permits a database containing both tenants. Does Table 53.3 establish tenant isolation? Propose the necessary separate test.
53.9 Reference answers
Section titled “53.9 Reference answers”- South’s cached
shippedresult can be returned to north/Mira. Partition cache access by trusted tenant and authorized subject/scope as appropriate; validate current authorization on retrieval. Include object/source version and relevant request conditions so stale or differently scoped entries are not reused. A cache hit is not an exemption from access checks. - No. Audience validation fails before the requested scope or approval can authorize this service. An approval does not repair an invalid credential; obtain a valid service-specific authority through the host’s authentication path.
- Reject the stale version or request a fresh review of the changed action context. The approved object state has changed; the version guard prevents an old approval from silently covering it.
- The downloaded bytes may match the download page’s checksum. That does not establish who controls the page, whether the helper was reviewed, or whether its build is trustworthy. Compare against separately trusted expectations and review the update before changing them.
- No. A process may misuse everything it can legitimately read. Test identical IDs across two tenants, authenticated owner checks, cross-tenant argument rejection and cache separation at the application/storage boundary. The OS probe answers a different question.
Part checkpoint answers.
- The host’s deterministic transition and budget rules stop or abstain; empty evidence leads to
ABSTAINEDwithout a model call in our workflow. Keep the reason and observed evidence state. A model request cannot extend the graph or budget. - Reject the mismatch, review origin, instructions, scripts, assets and dependencies, then deliberately approve a new version/inventory and run its tests. A familiar name does not approve changed behavior.
- Inspect a durable receipt tied to tenant, subject, action and idempotency key, then reconcile current state. In our single database, effect and receipt commit together. A new key can represent a new action and duplicate a remote side effect; replaying saved events executes nothing.
- Request-level outcomes plus retrieval, packed context, proposed answer/support and tool trajectory show the first observed failure. Equal totals discard those positions. Rerun the affected and downstream checks after repair.
- The policy owner investigates source correctness; the service owner applies the resulting source/index/cache change. Preserve both current 750 and valid historical 600 under their respective dates. Citation matching alone cannot settle the dispute.
- Reject the unexpected tenant argument and scope storage/cache access to the authenticated tenant and authorized owner. Check audience, expiry and operation scope as well. Confinement neither changes identity nor grants cross-tenant access.
53.10 References
Section titled “53.10 References”- MCP authorization, 2026-07-28 and official security best practices, scope/audience, confused-deputy and token-passthrough sections. These support service-specific authority and independent authorization checks; the local principal is not OAuth.
- Agent Skills specification, resources and progressive disclosure. Packaging reusable instructions does not establish trusted origin or execution permissions.
- OWASP GenAI 2025, LLM03 Supply Chain, mitigation items 5–9. These motivate inventories, licenses, integrity and continuing review across dependencies and model assets.
- SLSA 1.2, Verifying artifacts, verification steps and forming expectations. Authentic build provenance requires more than a matching artifact digest; this project claims no SLSA certification.