Permissions¶
Two rules say what reaches whom. A channel rule says whose turns may read a channel and who may write in it. An agent rule says where an agent may run. Every limit below follows from these two.
daimon.core.permissions is the model. It reads the stored policy as rules,
writes rules back, and derives every limit from them.
daimon.core.authz.authorize decides through it and adds who is asking:
admin exemptions and agents it couldn't resolve. Code that only asks whether
any rule exists, or where, asks the model (any_own_readers,
any_readers_limited, home_of, ruled_agents and the like), never the
stored rules. Tests check the model against authorize over combinations of
rules on two channels, a thread, a category and two agents with rules.
Channel rules¶
| Field | Value | Meaning |
|---|---|---|
readers |
any |
any turn, from anywhere (platform permissions still apply) |
inside |
only turns inside the channel | |
own |
only the channel's own agents, inside it | |
writers |
any |
any agent answers and posts |
own |
only the channel's own agents answer and post | |
none |
no turn starts and nothing posts, admins and daimon's notices included |
A channel's own agents are those whose agent rule names it alone, while
its readers are own: that channel is their home. own goes on both
fields, or on readers with writers: none. A caller with no agent (the
CLI, an operator token) may still post where writers is own: that is
input, and the readers rule keeps it inside.
A rule sits on a channel or a Discord category, and a thread follows both; a
category only takes writers: none. Chat and the Permissions screens set
channel rules only: a thread id names its channel. The operator can still keep
one Slack or Discord thread to turns inside it (daimon channels rule set
--thread ID --readers inside); a thread takes the strictest of that, its
channel's and its category's rule.
What a channel rule limits¶
| Limit | readers: inside |
readers: own |
writers: none |
Model |
|---|---|---|---|---|
| Turns start | yes | own agents only | no | run_refusal |
| Agents that answer and post | any | own | none | run_refusal, post_refusal |
| Read from outside (channel, sessions, history) | no | no | yes | readable_from, session_readable_from |
| Sessions read and continued by | turns inside | own agents inside | anyone allowed | session_homes |
| Memory writable | no | own agents only | yes | memory_writable |
| Content kept inside | no | yes | no | keeps_content, held_to |
| People from another organisation answered | no | yes | no | answers_external |
| Agents listed there | all | own only | all | listed_at |
| Default agent | any | own only; never cleared | any | binding_refusal, clear_refusal |
| Charged to its budget | its turns | its turns and its own agents' calls | its turns | budget_channel |
Content kept inside means a call from a turn in the channel, whatever
agent runs it, reads only the channel and the conversations that ran there,
posts only inside it, sends no direct messages and creates no agents. A call
by the channel's own agent is held the same way, wherever it runs. Routines
saved there post only there and never DM their results
(keeps_routine_inside). Only the channel's setup thread may still
configure its own agent.
Publishing (a report, notebook or upload URL) puts content behind a link whoever holds it opens. From a channel whose content is kept inside, or by an agent with a rule, it waits for the requester to press Approve on a card. Agent keys and runs nobody watches can't publish there, and nobody changes daimon's server-wide name or avatar there. See publishing.
Listed covers agents, their skills, environments, routines, timers and defaults. From outside, a channel's own agents are hidden everywhere.
People from another organisation (a Teams guest the tenant doesn't list
as a member) are answered only in a channel with readers: own or its
threads, never in its setup thread or a DM.
A thread whose channel is unknown fails closed while any channel's readers
are own, since it may lie in one (home_unknown).
Agent rules¶
runs_in lists the only channels an agent runs in, threads included. It is
unset for an agent that runs wherever the agent cascade
sends it, and empty for one that runs nowhere. A rule on any name the agent
carries applies, so an agent with rules under two names runs only where both
allow.
| no rule | runs_in |
own agent (home) | |
|---|---|---|---|
| Runs | where the cascade sends it, outside readers: own channels |
inside runs_in, outside readers: own channels |
inside its home |
| Posts | where it runs and writers allow, and the requester's own DM |
the same | inside its home only |
| Direct messages | anyone | the requester only | none |
| Publishes | yes | once the requester approves | once the requester approves |
| Creates agents | yes | yes | no |
| Copied (fork) | by an admin | never | never |
| Listed | outside readers: own channels |
the same | inside its home only |
| Memory writable | where readers is any |
the same | also in its home |
| Charged to | the channel it runs in | the same | its home |
Publishing¶
A turn whose admission would refuse PUBLISH gets its session's publish
tools (PUBLISH_TOOLS) on always_ask, so the platform shows an Approve
card before the call runs. The MCP server checks for itself: it reads the
live session (_session_gate.session_asks_first) and only then authorizes
with approved=True. A session without that card, an agent key or a run
nobody watches is refused, so a plumbing failure refuses rather than
publishes.
Exemptions¶
- Server admins are exempt where the output reaches them alone: their own
DM and hub turns, and hub reads of sessions inside limited channels. They
configure agents with rules from anywhere and see every agent listed.
Agent rules and
readers: ownstill hold for them in channels, threads, routines and handoffs. Nothing writes wherewritersisnone. - Channel admins of a
readers: ownchannel have the same exemption for its own agents. They configure an agent whose rule names only channels they administer, and read those channels' sessions from the hub. In Discord and Slack chat, the agent is told when the requester administers the current channel, so it can try the requested setup tool. Each tool still checks the target agent's reach before changing instructions, skills or keys. - A setup thread under a
readers: ownchannel answers as the built-in agent.
Agent keys and tokens with no person behind them are never exempt. Trust model has the full table.
Changing the rules¶
Rules are server or workspace admins' calls, never a channel admin's:
- chat:
set_channel_ruleandset_agent_rule(MCP tools); - panels: the channel's Permissions screen, from Who answers where in the Discord and Slack setup panels, and the Channel settings dialog on Teams;
- CLI:
daimon channels rule setanddaimon agents rule set.
Setting readers: own makes the channel's default agent its own (a custom
agent answering nowhere else), or copies an agent for it (copy_from).
Moving readers off own keeps its agents' rules, so they still run only
there, unless release_agents drops them. Every write goes through
with_channel_rule and with_agent_rule, which refuse a rule nothing would
enforce. archive_channel_copy retires a copy when its channel closes.
daimon tenants access-policy rules PLATFORM WORKSPACE_ID [--json] lists
every channel, category and agent rule, and each agent's home.
Stored policy. The policy row holds channel_rules, category_rules and
agent_rules. A row an older build wrote (lists of protected, sealed and
isolated channels and channel pins) reads as the same rules and is rewritten
in the new shape on its next change, so no migration runs.
The invoker allowlist, the Teams guests counted as members, read-only DM memory, channel admins, environments, budgets and operator tokens are not channel or agent rules. They say who may act and who may change the rules.