Security model
Wayglass applies Zero Trust to code changes: trust is decided per change, from what the change is, and never granted to an agent as a standing permission. This page lists the mechanisms that make that hold, and the places where it does not hold yet.
Tokens
Section titled “Tokens”All repo access goes through Artifacts repo tokens that the Worker mints for one repo, with an explicit short lifetime.
| Token | Repo | Scope | Lifetime | Who gets it |
|---|---|---|---|---|
| Init token | The new trunk | write | 900 s | wayglass init, once, to push the first commit |
| Fork token | One intent’s fork | write | 900 s | The agent that declared the intent; refreshed on every push, older ones revoked |
| Land token | The trunk | write | 300 s | wayglass land, only for an approved decision whose fork has not moved; revoked as soon as the land is verified or aborted |
| Session fork token | The new fork made by session fork |
write | 900 s | The person or agent forking the session; the source fork’s token is never handed out |
So no agent holds a write token for the trunk except for the few seconds a land takes, a fork token works only on its own fork (the trunk refuses it), and only one land per project can hold a trunk token at a time.
The CLI never writes a token to disk. Tokens reach git through GIT_CONFIG_COUNT, GIT_CONFIG_KEY_n and GIT_CONFIG_VALUE_n environment variables as an http.extraHeader, so they appear neither in the remote URL, nor in argv (where ps would show them), nor in .git/config. The mirror push in land uses your own git credentials, never an Artifacts token.
The Worker itself holds no Cloudflare API token with access to Artifacts; it reaches Artifacts only through its Workers bindings. The one optional API token, CF_EVENTS_API_TOKEN, is limited to Queues › Edit and is used only to subscribe new trunks to push events.
Server-side policy
Section titled “Server-side policy”The decision that counts is made on the Worker, not on the client:
- The Worker reads the fork head and the base commit from Artifacts, computes the diff itself, and classifies it.
- It runs the same Go policy evaluator the CLI uses for its preview, compiled to WebAssembly, so the two cannot drift.
- It reads
.wayglass/policy.yamlfrom the trunk at the commit the intent forked from, never from the fork. Editing the policy in a fork does not change how that fork is judged. - Any change under
.wayglass/goes to a person at minimum (builtin:protect-policy). So does a change too large to read in full, and a change judged against a missing or invalid policy file.
What the Worker never takes from the client
Section titled “What the Worker never takes from the client”| The client sends | The Worker uses instead |
|---|---|
| Tags, matched rule, action, reason from the local preview | Its own classification and decision. The client’s preview is stored only for comparison and is never shown on public routes |
| A claimed commit | The fork head it reads from Artifacts; propose fails if they differ |
| A jurisdiction | The jurisdiction of the namespace the project lives in |
| “I landed it” | A check that the trunk now holds exactly the approved commit, or a merge of it touching only the decided paths. A mismatch is recorded as land.mismatch |
No self-approval
Section titled “No self-approval”The agent that declared an intent cannot approve or reject its decision; the Worker refuses with conflict. multi_approve counts distinct approvers. Every verdict needs a reason, and placeholder reasons (TODO, n/a, <what you checked> and similar) are refused, because the audit log keeps them forever.
Only the agent that declared an intent can get a fresh fork token for it, and only that agent or the admin can abandon it (wayglass intent abandon, with a reason). Abandoning revokes the intent’s live fork tokens and refuses new ones; the fork repo is kept.
Identity
Section titled “Identity”There are two kinds of key:
- The admin key (the Worker secret
WAYGLASS_ADMIN_KEY). It can do everything, including minting agent keys, and its caller names itself withX-Wayglass-Agent(WAYGLASS_AGENT). - Agent keys (
wgk_…), minted by the admin withwayglass keys create --agent <name>. Each is bound to one name. A request made with one is recorded under that name on every route; the Worker ignoresX-Wayglass-Agentand any name in the request body, so an agent key cannot approve its own change under another name. The Worker stores only a SHA-256 of each key;wayglass keys revoketakes effect on the next request.
wayglass doctor shows which kind of key you are logged in with and the name it acts as (from GET /v1/whoami).
The audit log
Section titled “The audit log”Every intent, push, overlap, decision, verdict, token issue, land and abort is appended to a per-project log in a Durable Object. There is no route to edit or delete entries. The same decision record is written into a git note on the landed commit.
Known limits
Section titled “Known limits”- The admin key still names itself. Whoever holds it can act as any agent name. Give it only to the person who runs the Worker, and give every agent and every other person an agent key from
wayglass keys create --agent. The self-approval check is only as strong as the keys you hand out. - Agent keys are long-lived and not scoped to a project. A key works on every project on its Worker until it is revoked. There are no short-lived session tokens exchanged for a key yet, and minting a key is not recorded in any project’s audit log.
- A declared model family is self-reported. A policy rule can require an approver from another model family; the Worker compares the families the author and the approver declared, not ones it can verify.
- Artifacts has no branch protection. Someone with a trunk write token could push something other than the approved change. The Worker detects that after the fact (
land.mismatch, and trunk push events from the event subscription) but cannot prevent it. - Policy at the fork base. A change that forked before a policy change is judged by the older policy, even if it lands after the new one.