Workspace Trust
Wayland Core reads project config from .wayland-core.toml in your working directory. From v0.12.26, the keys in that file that can expand what the engine is allowed to do are honoured only in a trusted workspace. In an untrusted one they are stripped before the config is applied.
The reason is direct: cloning a repository should not be able to grant that repository an MCP server, a provider, or a hook that runs on your machine.
What gets stripped
Section titled “What gets stripped”In an untrusted workspace, these tables and keys are removed from project config before it is applied:
| Key | Why it is authority-expanding |
|---|---|
[profiles.*] | Selects providers, models, and their settings |
[mcp.servers.*] | Declares external tool servers the engine will connect to |
[providers.*] | Declares provider endpoints and how they are reached |
| Hooks | Run commands at engine lifecycle points |
env_passthrough | Decides which environment variables reach subprocesses |
| Sandbox keys | Govern shell containment |
Everything else in project config is applied normally. A workspace you have not trusted is still fully usable; it simply cannot widen the engine’s authority on its own say-so.
Granting trust
Section titled “Granting trust”Trust a workspace once:
wayland-core --trust-workspaceThe grant is recorded against the workspace’s executable surface, not against its path. That distinction is the whole point: a path is a name, and a name can come to point at different content.
The refusal names itself
Section titled “The refusal names itself”Before v0.13.0, an untrusted workspace failed in a way that was almost impossible to diagnose. Running --profile p reported:
Profile 'p' not found in configfor a profile that was plainly present in a file Core had just parsed and then discarded on the trust decision. The only true account of what happened was a tracing::warn! invisible at default verbosity.
From v0.13.0 the refusal names the trust decision and the remedy instead. A profile that genuinely was never written still reads as absent, so the clearer message cannot send you chasing --trust-workspace over a typo.
Symlinks in the executable surface
Section titled “Symlinks in the executable surface”A workspace whose executable surface contains symlinks is trustable. The fingerprint follows each link and hashes the bytes it resolves to.
This matters for hosts that compose a workspace out of their own assets. Wayland Desktop links builtin and user skill directories into a per-chat workspace, and that workspace can obtain trust.
The property that makes this safe is what happens on change:
- Rewriting a link’s target voids the grant.
- Repointing a link at different content voids the grant.
Trust cannot outlive a change to what actually executes.
Dangling links, link cycles, and links pointing at devices or FIFOs still fail closed.
Related limits that also tightened
Section titled “Related limits that also tightened”Two neighbouring rules landed in the same release and are worth knowing together with this one:
- Project config can no longer raise
max_tokensormax_turns. An untrusted repository cannot widen limits you set. Raise them in your own config instead. - A project
[memory]block cannot defeat a global memory opt-out. Cloning a repository can no longer silently re-enable memory recording.
The egress master switch is operator-owned for the same reason: a project cannot negotiate it.
If you host the engine
Section titled “If you host the engine”There is a second change in v0.12.26 that affects hosts rather than config authors. A runtime MCP server added over the json-stream protocol is now bound to the assistant identity that declared it, and a session with no identity to bind to refuses the declaration:
active assistant identity is required for a runtime MCP declarationSupply an identity at spawn with --assistant NAME, or WAYLAND_ASSISTANT=NAME in the environment. Any stable name works; it is the same identity only_for_assistant matches against in config. It is not --agent, which selects a persona and a system prompt.
The refusal is announced on an error frame and an mcp_failed frame naming the server and the reason, and neither is fatal. The engine goes on to serve the session normally, so a host that does not render mcp_failed shows a conversation with no MCP tools and no visible cause. If you host the engine, render mcp_failed.
See Driving the Engine from a Host and MCP Client.