Workspace/Lesson workspace
Loading progress
Reliable systems45 min

Secret handling and approvals bound to exact actions

Lesson 3 of 3
How to study this lesson

Read one section, trace the worked example, and try the knowledge check. Bookmark saves a shortcut; notes appear in My notebook. Mark lesson complete is your own assessment and does not mark its lab passed.

By the end, you can

  • Keep service credentials out of model-visible context.
  • Bind approval to a canonical action and detect stale proposals.
  • Design expiry, audit, and revocation without leaking sensitive payloads.

Separate the decision from the credential

A model that drafts a database query does not need to see the database password. The application can authenticate to a broker that validates the query scope and uses a restricted service identity. Keeping secrets out of prompts reduces opportunities for accidental reproduction, logging, or injection-driven disclosure. It also prevents a copied conversation from becoming a credential distribution channel.

Store credentials in a system intended for secret management, with access control, rotation, and audit appropriate to the deployment. Environment variables are one delivery mechanism, not a complete secret-management design. A process can often inspect its own environment, and child processes may inherit it. When launching a worker, construct the allowed environment explicitly. Do not assume that removing secrets from the final assistant response prevents them from appearing in traces or subprocess output.

A secret has a lifecycle

Follow a credential from creation through distribution, use, rotation, revocation, and deletion. At each stage, ask who can access it and what evidence is recorded. Short-lived credentials can limit exposure after a worker finishes, while a narrowly scoped credential limits the actions possible before expiry. These are complementary controls. A short-lived administrator credential remains powerful during its lifetime.

Consider a synthetic reporting worker that needs five minutes to fetch one dataset. A broad credential valid for 30 days grants both excessive duration and excessive scope. A brokered read for the dataset, or a short-lived read-only grant restricted to that resource, is a better fit. Logs can record credential identity or a nonsecret reference. Avoid logging token values, request headers containing credentials, or raw prompts that unexpectedly include them. Redaction is a fallback, not the primary separation mechanism.

Approval refers to a concrete proposal

A user approving send this report should be shown the recipient, attachment, message, and relevant scope. After approval, the executor must verify that the action still matches what was reviewed. A generic approved boolean cannot establish which content was approved and can be accidentally reused for a changed proposal. Represent an approval as a record bound to an action identifier and a canonical representation of the consequential fields.

In the example, canonical JSON sorts object keys and uses fixed separators before hashing. A changed recipient produces a different digest, invalidating the previous match. The digest is a comparison mechanism, not proof that a human approved anything. A production approval record must come from an authenticated interaction, have an expiry or revocation policy, and be protected from modification by the agent. Do not let the model manufacture its own approval record.

Close the gap between review and execution

Suppose the user approves modifying report revision 8, but revision 9 appears before execution. Even if the patch text is unchanged, applying it against the new revision could produce a different outcome. Bind the approval to a resource version or precondition and recheck immediately before committing. This is a time-of-check to time-of-use problem: a once-valid decision can become stale when the world changes.

A toy approval can include actor A, tenant blue, file report-7, revision 8, and action hash H. Execution succeeds only when all those values still match the trusted request and current state. If revision 9 is present, prepare a new concrete proposal for review or use a previously authorized policy for reconciling harmless changes. Repeatedly asking for approval without first resolving the actual change wastes the user's attention.

Make review selective and auditable

Human approval is not useful if every low-impact internal step produces an indistinguishable dialog. Define the consequential boundary and gather enough context before asking. Show the actual change, explain its effect, and preserve authorization already granted by the user. An approval system should support denial, expiry, cancellation, and changed proposals without confusing these outcomes with execution errors.

Audit the chain from authenticated user intent to proposal, approval record, execution attempt, and receipt. Use stable identifiers so investigators can connect records without exposing sensitive content in every log. In the lab, a trusted set of action hashes stands in for real approval records to keep the exercise standard-library-only. It does not implement authentication, secure storage, cryptographic signatures, or a user interface. Its purpose is to make exact matching and least privilege testable before integrating a real identity and approval system.

Work through the code

Synthetic actions are hashed using canonical JSON, then compared with a trusted toy approval record and expiry. No email is sent. The record has no authentication or signature and must not be treated as a secure approval token. Change any consequential field to see how the binding behaves.

m22_lesson_3.py
python
import hashlib
import json

def digest(action):
    raw = json.dumps(action, sort_keys=True, separators=(",", ":"),
                     allow_nan=False).encode("utf-8")
    return hashlib.sha256(raw).hexdigest()

def matches(action, approval, now):
    return (approval["expires_at"] > now
            and approval["hash"] == digest(action))

action = {"tool": "send", "recipient": "review@example.test",
          "attachment": "report-v8", "tenant": "blue"}
approval = {"hash": digest(action), "expires_at": 20}
changed = dict(action, recipient="other@example.test")
print("original:", matches(action, approval, 10))
print("changed:", matches(changed, approval, 10))
print("expired:", matches(action, approval, 20))
EXPECTED / ILLUSTRATIVE OUTPUT
original: True
changed: False
expired: False

Run Python snippets locally with the prerequisites named above. The module coding lab runs directly in your browser.

Pause and reason

An agent has a valid approval hash for publishing a report, but the stored report was edited after approval. The publish request contains only its filename. What must change?

Check your understanding

What does a matching SHA-256 action digest prove in the toy example?

Your notes

Explain the mechanism in your own words. Add a failure you want to test.

Saved notes appear in your notebook

Go deeper with primary sources

Practice this module