Hand this to an AI agent. It sets up act-as for Eva, Alice, and Carol on the HR application, then shows different answers from the same UI.
hr-assistant to a delegated agent, delegated-hr. Eva (HR), Alice (self), and Carol (peer) click the same buttons and get different answers. The app has no logic that knows the difference.
To do this, you will:
1
Register a delegated agent
Configure act-as verification on
delegated-hr and make it mandatory. Leave hr-assistant standing.2
Write rules about the person
Deploy Cedar that reads groups and compares
act_as.sub to tool arguments.3
Check the identities
Simulate Eva, Alice, and Carol before making a live call.
4
How this application implements it
Keep the token and act-as secret server-side. Sign per request.
5
Verify the results
See different answers from one UI, and each in audit.
Background
Earlier tutorials usedhr-assistant standing. This one registers a second agent, delegated-hr, that requires act-as. Cedar reads principal.act_as, so Eva, Alice, and Carol can get different answers for the same tool.
You register a second agent rather than flipping
hr-assistant to require act-as. Those settings would break earlier tutorials that call with no person attached. delegated-hr is the application agent that borrows each end user’s identity.
Act-as carries that person on each call. The boundary is enforced at the gateway, not in the model’s prompt.
That only works if your backend talks to Dome. Keep the token and signing secret on the server; the browser only says who is using the app. A frontend that called Dome directly could forge or omit act-as.
Prerequisites
For this tutorial, you will need:- Govern your first agent completed, with its sandbox still active. This tutorial reuses that workspace, its
demo-hrconnection, and its Default gateway. - Call a model through a pool completed if you want Chat under
delegated-hr(theemployee-summarypool). - Build a governed app completed, or the demo-hr-desk repo cloned as below.
- Node.js 18 or later.
sandbox-get-started. Switch back if it does not:
cd into that directory. You will check out this tutorial’s branch when you wire act-as.
This tutorial runs entirely in a sandbox. In a production workspace, the identity provider is wired once by an operator, and the rules that read its claims are owned by security.
Register a delegated agent
Registerdelegated-hr rather than reusing hr-assistant. Standing vs delegated is chosen per agent. hr-assistant stays the standing editor/curl agent from earlier tutorials. delegated-hr is the purpose-built application agent: it authenticates as itself, but every governed call must present a verified end user, and Cedar authorizes on that person.
This tutorial uses HMAC — a shared secret your backend uses to sign act-as. That is fine for a sandbox. In production, use OIDC instead: register with --actas-method oidc and forward a JWT from your identity provider so Dome verifies the person against the IdP, not a secret your service holds.
Generate the signing secret first so you can save it:
--actas-method hmactells the gateway how to verify the identity your service presents. Shared-secret HMAC is the sandbox path here; production usesoidc.--actas-requiredrejects any call arriving without an identity. Without it, act-as is accepted when present and ignored when absent, and a caller who omits it gets the agent’s own permissions.
--actas-allowed-group, --actas-allowed-email, and --actas-allowed-subject add an admission-level allowlist, rejecting identities outside it before Cedar runs. That is a coarse gate for narrowing which population an agent may serve. Leave it unset here so Cedar makes every decision.Write rules about the person
Cedar sees the end user asprincipal.act_as, a record carrying sub, email, roles, groups, and any custom claims the verified identity provided. Rules can read those the same way they read anything else.
This policy does four things:
- Anyone with a verified identity can list employees and view the org chart
- Someone in the
hrgroup can open any employee record and read salary - Anyone else can open only their own record (
act_as.submatchesemployee_id) - Calls with no act-as identity, and tools outside that set, are denied
delegated-hr.cedar. Create it (or copy from the clone):
delegated-hr.cedar
principal has act_as guard matters. act_as is absent from the entity entirely when no identity was verified, and reading an absent attribute is an evaluation error rather than a false. Guard it, and an anonymous call falls through to a clean deny.
Self-lookup compares resource.arguments.employee_id to principal.act_as.sub. Demo personas set sub to the employee id (E005 Eva, E001 Alice, E003 Carol), so Alice can open her own record while Carol cannot open Alice’s. Payroll (get_salary) stays HR-only.
If you completed Call a model through a pool, Chat still needs llm:invoke on employee-summary. Tool rules do not cover inference. Create delegated-hr-llm.cedar (same shape as hr-assistant-llm.cedar, scoped to this agent):
delegated-hr-llm.cedar
dome rules apply at the same agent scope replaces the previous bundle rather than layering beside it.
Check the identities
Simulation accepts an end-user identity, so you can test a rule that readsact_as without a signing secret, a service, or a live call. Pass --eval-arguments whenever the rule compares employee_id.
Eva (HR) looking up Alice:
ALLOW. Alice looking up herself (sub E001):
ALLOW. Carol looking up Alice:
DENY. Confirm salary is HR-only:
ALLOW then DENY. Confirm that the directory stays open to everyone by swapping the resource for demo-hr/hr/list_employees, and that omitting the act-as flags entirely denies the employee lookup rather than erroring.
This is the loop worth keeping. A rule that reads identity claims has more branches than a rule that reads a tool name, and simulation is where you find the branch you forgot.
How this application implements it
Act-as is a server-side concern. The gateway authenticates the agent withAuthorization: Bearer dome_… and the end user with X-Dome-Act-As. Both headers are secrets your browser must never hold.
Same frontend/backend split as Build a governed app. With act-as the stakes are higher: if the browser held the agent token or signing secret, anyone with DevTools could impersonate anyone.
Check out this tutorial’s branch, which adds the Act as picker and HMAC signing, then point the server at
delegated-hr:
.env with the delegated-hr token and the secret you generated above. These values are read only by the Node process, never shipped to Vite or the browser:
.env
How the application signs
The browser only sends a persona id (eva | alice | carol). The Hono proxy maps it to claims, signs with the server secret, and sets X-Dome-Act-As on the outbound Dome request. Cedar never sees a branch in App.tsx.
Personas are fixed claims for the demo. There is still no allow/deny logic in the UI:
server/actas.ts
signHMACActAs builds the envelope the gateway verifies: identity fields + _ts, HMAC-SHA256 over canonical JSON | timestamp, then standard base64 of the signed payload. That string is the X-Dome-Act-As header value.
server/actas.ts
redactActAs is only for the View gateway call sheet. It truncates the header in the UI trace so the signed blob is not dumped into the browser transcript. Dome still receives the unredacted value.
server/dome.ts
server/types.ts
DOME_ACTAS_SECRET is set.
Click through Eva, Alice, and Carol
Stay on Tools for the clearest contrast:- Select Eva Martinez (HR ·
hr· E005). - Click List employees. Allowed.
- Click Who is E001?. Allowed. Email arrives as
[REDACTED](Filter still applies). - Click What is Alice’s salary?. Allowed (HR-only).
- Select Alice Johnson (Engineering · E001).
- Click Who is E001?. Allowed (self:
submatchesemployee_id). Email still redacted. - Click What is Alice’s salary?. Denied.
- Select Carol Williams (Engineering · E003).
- Click Who is E001?. Denied. Open View gateway call and confirm
X-Dome-Act-Ason the request. The response carries the Cedar reason.
One UI, one agent, one credential, three answers. Alice can still see herself. Carol cannot open Alice’s record. Only Eva reaches payroll.
For a real identity provider, configure the agent with
--actas-method oidc and forward the JWT your application already validated as X-Dome-Act-As. The gateway verifies the signature against the provider’s discovery document, so your service never becomes the authority on who the caller is. HMAC is the sandbox path. Refer to Use OIDC or bound act-as (coming soon) for the production methods.Troubleshooting
Troubleshooting
These are remediation steps if you get stuck:
-
The Act as bar does not appear.
DOME_ACTAS_SECRETis missing or empty. Restartnpm run devafter writing.env. Confirm/api/configreturns"actAsEnabled": true. -
400withact-as header required. The agent is registered with--actas-requiredand the call carried no identity. Confirm the app is sending a persona and that the secret is set so the proxy signs. -
403withact-as verification failed. The signature did not verify. Usually the secret in.envis not the one the agent holds. Rotate both to a known value: -
403withagent act-as method does not meet workspace policy. The workspace requires a stronger verification method than this agent uses, typicallyoidcrather thanhmac. Check the policy before weakening anything: -
Both users get the same answer.
The rule is not reading the claim you think it is, or you are still on the
hr-assistanttoken. Simulate both identities again and confirm.envuses thedelegated-hrkey: -
A
VITE_env var or browser network tab shows calls to the Gateway / holdsdome_…. Wrong shape. Move the token and act-as secret back to server.envand call Dome only from the Hono proxy. The frontend may only hit/api/*on your origin. -
Chat fails with a pool / llm error while Tools work.
Apply the pool rule to this agent:
- The employee record arrives with a masked email for Eva or Alice. That is the response Filter from the first tutorial, still doing its job on this connection. Filters apply to the tool, not to the agent, so a second agent inherits them.
Verify the results
Confirm both decisions are on the record:authorization.act_as.rejected, because a failed signature is a different problem from a denied call. The first means your service or its secret is misconfigured. The second means the rules did their job.
Quotas take end users as subjects too.
dome model quota set --subject act-as --act-as E005 --limit 20 caps one person’s inference spend, which is how you stop a single runaway session from consuming a team’s budget.Clean up
This is the last tutorial in the Get Started track. Delete the sandbox to remove the agents, rules, and grants together:DOME_ACTAS_SECRET (or switch back to the hr-assistant token) if you return to earlier tutorials.
Next steps
You learned how to require verified end-user identity, authorize from the person, and attribute decisions to both agent and user. Continue with:- Delegated agents to configure verification providers and Act-As
- Simulate Rules to probe Act-As decisions before deploy