Agent Office v0.27.0 · POC Get started
Menu: Reference

Reference

access.yaml

Who the humans in your office are, what role each holds and where, and who may approve hires, access changes and budgets.

access.yaml lives at the root of the policy repo. Like personnel files, it changes only by a merged pull request. The office reads it on every reconcile (policy push webhook, boot, every 5 minutes), adds or updates the humans it lists, and records human.access.changed in the ledger. A human removed from the file is disabled and signed out (ledger human.disabled), never deleted; the owner from office.yaml is never disabled. Unknown keys and duplicate emails are validation errors (ledger policy.invalid); while the file is invalid, only the owner break-glass applies.

Example#

access.yaml
humans:
  - email: owner@example.com
    name: Owner Name
    roles: [{ role: owner, scope: office }]
  - email: lead@example.com
    name: Dana Lead
    roles: [{ role: lead, scope: "dept:engineering" }]
policy_roles:
  hiring_approvers: [owner@example.com]
  access_approvers: [owner@example.com]
  budget_approvers: [owner@example.com]

humans#

FieldTypeMeaning
emailemail (stored lowercase)The human's sign-in identity. Appears in the ledger as human:<email>.
namestringDisplay name.
roleslist of { role, scope }, default []Grants. A human can hold different roles on different scopes.

When the reconciler adds a human without a password, it creates a one-time invite (ledger human.invited). Every invite link is valid for 72 hours, however it was made. The link itself is never stored in the ledger. Admins (access.propose) see live invites under Pending invites on the People page, each with its copyable link, its expiry and New link, which replaces it with a fresh one (only one live link per person). People without a password also have an Invite link button, and on the host there is officectl invite <email>. The human opens /invite/<token> and sets a password.

Roles#

Roles nest. Each role can do everything the roles below it can.

RoleIn short
viewerSee the office, agents and runs.
operatorAlso chat with agents, send mail, start runs.
leadAlso read transcripts, stop runs, decide approvals, propose hires and capability changes.
adminAlso suspend and resume agents, propose access changes, read the ledger and settings.
ownerEverything.

The exact minimum role for every action is in the Permission catalog.

Scopes#

ScopeCovers
officeEverything in the office.
dept:<name>Every agent whose personnel file has department: <name>.
agent:<slug>That one agent.

A human's effective role for an action is the highest role among their grants whose scope covers the target. For example, lead on dept:engineering lets Dana kill runs of engineering agents, but not of agents in leadership.

policy_roles#

Who approves what, as lists of emails (default empty). These are separate from UI roles.

FieldApproves a policy change that touches
hiring_approversAn agent: a new hire, a charter, or any personnel field other than the budget (capabilities, status, manager, run settings).
access_approversaccess.yaml or connections/.
budget_approversAn agent's budget.

Enforced on every merge in the office. When someone merges a policy PR in the office (the PR page, or Approve & merge on Needs you), the office works out from the diff which kinds the change touches and refuses unless that person is listed for every one of them. A change to a budget and to capabilities needs someone who is both a hiring and a budget approver. The owner may always merge. The PR page says who can merge it, and pr.merged records the kinds in the ledger.

The forge underneath still applies branch protection: only the approvers team may merge the policy repo's main, and --require-approvals <n> at officectl init adds required reviews. The office keeps that team in step with these lists. People never sign in to Gitea, so in practice every merge goes through the office; a Gitea admin who merges in Gitea directly bypasses the per-kind check.

Owner break-glass#

The owner.email in office.yaml is always an owner, even when access.yaml is missing, invalid, or doesn't list them. A broken policy file can't lock the owner out.