One agent per working group, living in that group's Matrix room: group members watch, stewards
prompt it, and it posts what it did. Limited and appropriate permissions, transparency by
architecture, and no member data on the agent host.
Continues the work in #17 (closed): the steward bot moved onto co-op infrastructure and the
first non-steward agent, infrastructure-bot, is running isolated. This issue is about scaling
that to group bots deliberately rather than by accident.
Isolation model (decided 2026-10-26)
Three tiers, using the cheapest mechanism that is still honest:
Tier 1 — the steward bot (inferencebot). Own Unix user. Holds the co-op's master
credentials (gateway admin, Cloudron, the room-administration account) and is the
fleet-administration path.
Tier 2 — member-facing group bots. One Unix user for the tier (today the
infrastructure-bot user), one Hermes profile per bot, and one gateway process serving all
of those profiles. This is where the resource savings come from.
Tier 3 — anything with materially different privilege. A bot holding a group's staging-app
credential, or any bot with member-data access, gets its own Unix user, because its
credentials must not be readable by its siblings.
The rule that decides the tier: a bot may be a profile only if we would be comfortable with
it holding every other credential in that tier. Profiles of one user share a home, so one bot can
read another's configuration and keys. That is acceptable when the scope is genuinely identical,
and unacceptable when it is not.
Why profiles for tier 2 (measured on the agent host)
A separate agent costs ~3 GB of disk (runtime, source, tool installs) plus its own gateway
process at ~0.3–0.75 GB of RAM.
A profile adds only its own state — skills, sessions, logs, database — a few hundred MB, most
of it disposable cache.
One gateway process serves every profile on the host, so N group bots share one process
instead of needing N.
For four bots: separate users would be ~12 GB of disk and ~1.6 GB of RAM on a host with
~2.4 GB free, which does not fit. Profiles are ~1–2 GB of disk and one process. On this host
profiles are close to necessary, not merely cheaper.
Per-bot composition (the permission backplane)
Its own inference budget: one gateway team per bot (agent:<group>) with a model allowlist
and a monthly cap, so spend is attributable and revocable per bot.
Its own Matrix identity and room: the group's room, prompting limited to that group's
stewards, mention-required so it never speaks unprompted, isolated session lanes per steward,
and read-only digests for members.
Tool tiers: read-mostly toolsets in shared rooms; write access only against that group's
staging app; destructive operations only in operator messages, never in a shared room.
No member data on the host: harnesses stay stateless. Confidential member data stays on the
primary; a compromise here exposes keys, never member records.
The load-bearing control: tool restriction
Credential separation between profiles only means something if a bot cannot read its siblings'
files. A profile with arbitrary shell or filesystem access can read every other profile's
secrets, and a single injected instruction then reaches the whole tier. Tier 2 therefore requires
an explicit toolset allowlist per bot (no arbitrary filesystem or shell access) and a
deliberately small tier. Where we cannot restrict tools for a given deployment, that bot belongs
in tier 3.
Adding a bot (repeatable, ~15 minutes once the pilot proves it)
profile → scoped gateway team and key → Matrix account and group room → per-bot configuration
(room, steward allowlist, toolset) → isolation test (confirm through the bot's own tools that it
cannot read a sibling's credentials) → hand it to the group with the norms in docs#6.
Accepted trade-offs
One gateway serves the whole tier: a restart affects every group bot at once, and compromising
the tier user exposes all tier-2 credentials. Accepted in exchange for the resource savings,
with the tier kept small and its credentials low-privilege.
Attribution inside a tier rests on configuration rather than the kernel, so we keep per-bot
identities and steward-only prompting. Anything needing a hard guarantee belongs in tier 3.
First step, before building
Verify profile behavior empirically with a throwaway profile: that profiles share the runtime,
that the tier's gateway serves them, that each profile's environment and credentials are its own,
and that toolsets can be restricted per profile. A configuration that looked correct and was not
cost us a cycle already (#17); these numbers should be measured, not assumed.
Open decisions
Which group pilots first.
Who counts as a steward for each group, and where that list is maintained.
Per-bot budget and model tier.
What a group dissolving means for its bot (delete the profile, revoke the key, leave the room).
Whether to cap the tier user's memory so one runaway bot cannot starve the host.
## What we are building
One agent per working group, living in that group's Matrix room: group members watch, stewards
prompt it, and it posts what it did. Limited and appropriate permissions, transparency by
architecture, and no member data on the agent host.
Continues the work in #17 (closed): the steward bot moved onto co-op infrastructure and the
first non-steward agent, `infrastructure-bot`, is running isolated. This issue is about scaling
that to group bots deliberately rather than by accident.
## Isolation model (decided 2026-10-26)
Three tiers, using the cheapest mechanism that is still honest:
- **Tier 1 — the steward bot (`inferencebot`).** Own Unix user. Holds the co-op's master
credentials (gateway admin, Cloudron, the room-administration account) and is the
fleet-administration path.
- **Tier 2 — member-facing group bots.** One Unix user for the tier (today the
infrastructure-bot user), **one Hermes profile per bot**, and one gateway process serving all
of those profiles. This is where the resource savings come from.
- **Tier 3 — anything with materially different privilege.** A bot holding a group's staging-app
credential, or any bot with member-data access, gets its own Unix user, because its
credentials must not be readable by its siblings.
**The rule that decides the tier:** a bot may be a profile only if we would be comfortable with
it holding every other credential in that tier. Profiles of one user share a home, so one bot can
read another's configuration and keys. That is acceptable when the scope is genuinely identical,
and unacceptable when it is not.
## Why profiles for tier 2 (measured on the agent host)
- A separate agent costs ~3 GB of disk (runtime, source, tool installs) plus its own gateway
process at ~0.3–0.75 GB of RAM.
- A profile adds only its own state — skills, sessions, logs, database — a few hundred MB, most
of it disposable cache.
- One gateway process serves every profile on the host, so N group bots share one process
instead of needing N.
- For four bots: separate users would be ~12 GB of disk and ~1.6 GB of RAM on a host with
~2.4 GB free, which does not fit. Profiles are ~1–2 GB of disk and one process. On this host
profiles are close to necessary, not merely cheaper.
## Per-bot composition (the permission backplane)
- **Its own inference budget:** one gateway team per bot (`agent:<group>`) with a model allowlist
and a monthly cap, so spend is attributable and revocable per bot.
- **Its own Matrix identity and room:** the group's room, prompting limited to that group's
stewards, mention-required so it never speaks unprompted, isolated session lanes per steward,
and read-only digests for members.
- **Tool tiers:** read-mostly toolsets in shared rooms; write access only against that group's
staging app; destructive operations only in operator messages, never in a shared room.
- **No member data on the host:** harnesses stay stateless. Confidential member data stays on the
primary; a compromise here exposes keys, never member records.
## The load-bearing control: tool restriction
Credential separation between profiles only means something if a bot cannot read its siblings'
files. A profile with arbitrary shell or filesystem access can read every other profile's
secrets, and a single injected instruction then reaches the whole tier. Tier 2 therefore requires
an explicit toolset allowlist per bot (no arbitrary filesystem or shell access) and a
deliberately small tier. Where we cannot restrict tools for a given deployment, that bot belongs
in tier 3.
## Adding a bot (repeatable, ~15 minutes once the pilot proves it)
profile → scoped gateway team and key → Matrix account and group room → per-bot configuration
(room, steward allowlist, toolset) → isolation test (confirm through the bot's own tools that it
cannot read a sibling's credentials) → hand it to the group with the norms in
[docs#6](https://git.inference.coop/co-op/docs/pulls/6).
## Accepted trade-offs
- One gateway serves the whole tier: a restart affects every group bot at once, and compromising
the tier user exposes all tier-2 credentials. Accepted in exchange for the resource savings,
with the tier kept small and its credentials low-privilege.
- Attribution inside a tier rests on configuration rather than the kernel, so we keep per-bot
identities and steward-only prompting. Anything needing a hard guarantee belongs in tier 3.
## First step, before building
Verify profile behavior empirically with a throwaway profile: that profiles share the runtime,
that the tier's gateway serves them, that each profile's environment and credentials are its own,
and that toolsets can be restricted per profile. A configuration that looked correct and was not
cost us a cycle already (#17); these numbers should be measured, not assumed.
## Open decisions
- Which group pilots first.
- Who counts as a steward for each group, and where that list is maintained.
- Per-bot budget and model tier.
- What a group dissolving means for its bot (delete the profile, revoke the key, leave the room).
- Whether to cap the tier user's memory so one runaway bot cannot starve the host.
First step done (2026-10-26): profiles verified on the tier-2 user. The numbers hold, and one control turns out to be mandatory rather than nice-to-have.
Cost: ~5 MB per profile, not ~3 GB. A profile has no runtime of its own — it shares the parent's — so it adds only state (a skills copy, empty DBs, logs). A separate agent costs ~3 GB of disk plus ~375 MB of RAM for its own gateway process.
Adding or removing a profile needs no restart. The running gateway adopts it live and drops it live ([MULTIPLEX] … serving every profile from this process (no restart), later Profile 'X' unserved), and it re-scans a profile when its config or .env changes. So a new group bot — including its credentials — can be added without touching the gateway at all.
Per-profile secrets are its own. Each profile gets its own .env and config.yaml. Cloning deliberately leaves messaging tokens and allowlists behind, so a cloned bot has no Matrix credentials until its own are added — which is what we want anyway.
The toolset restriction is the real boundary, and it binds. Defaults grant the full toolset (terminal, file, computer_use…). Restricted to the documented read-only set, a profile asked to read a sibling's secrets could not: it searched for a file-reading tool, found nothing available — the on-demand tool catalog is restricted too — and reported no file access.
The weakness it compensates for is confirmed: any process running as that user reads the parent's .env. Profile separation is credential organisation; the toolset allowlist is the control. Every group-bot profile must therefore carry an explicit read-only toolset — a provisioning step, not a nicety.
Consequences for the build-out:
The isolation test is now specific: ask a bot, through its own tools, to read another profile's secrets, and confirm it cannot (including via the on-demand catalog).
The accepted trade-off narrows: inside a tier, credentials are mutually readable by design of the mechanism, so every tier-2 credential must stay low-privilege and the tier must stay small.
Since no restart is needed to add a bot, the operator-only restart constraint does not gate the rollout.
**First step done (2026-10-26): profiles verified on the tier-2 user.** The numbers hold, and one control turns out to be mandatory rather than nice-to-have.
- **Cost: ~5 MB per profile, not ~3 GB.** A profile has no runtime of its own — it shares the parent's — so it adds only state (a skills copy, empty DBs, logs). A separate agent costs ~3 GB of disk plus ~375 MB of RAM for its own gateway process.
- **Adding or removing a profile needs no restart.** The running gateway adopts it live and drops it live (`[MULTIPLEX] … serving every profile from this process (no restart)`, later `Profile 'X' unserved`), and it re-scans a profile when its config or `.env` changes. So a new group bot — including its credentials — can be added without touching the gateway at all.
- **Per-profile secrets are its own.** Each profile gets its own `.env` and `config.yaml`. Cloning deliberately leaves messaging tokens and allowlists behind, so a cloned bot has no Matrix credentials until its own are added — which is what we want anyway.
- **The toolset restriction is the real boundary, and it binds.** Defaults grant the full toolset (terminal, file, computer_use…). Restricted to the documented read-only set, a profile asked to read a sibling's secrets could not: it searched for a file-reading tool, found nothing available — the on-demand tool catalog is restricted too — and reported no file access.
- **The weakness it compensates for is confirmed:** any process running as that user reads the parent's `.env`. Profile separation is credential *organisation*; the toolset allowlist is the control. Every group-bot profile must therefore carry an explicit read-only toolset — a provisioning step, not a nicety.
Consequences for the build-out:
- The isolation test is now specific: ask a bot, through its own tools, to read another profile's secrets, and confirm it cannot (including via the on-demand catalog).
- The accepted trade-off narrows: inside a tier, credentials are mutually readable *by design of the mechanism*, so every tier-2 credential must stay low-privilege and the tier must stay small.
- Since no restart is needed to add a bot, the operator-only restart constraint does not gate the rollout.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What we are building
One agent per working group, living in that group's Matrix room: group members watch, stewards
prompt it, and it posts what it did. Limited and appropriate permissions, transparency by
architecture, and no member data on the agent host.
Continues the work in #17 (closed): the steward bot moved onto co-op infrastructure and the
first non-steward agent,
infrastructure-bot, is running isolated. This issue is about scalingthat to group bots deliberately rather than by accident.
Isolation model (decided 2026-10-26)
Three tiers, using the cheapest mechanism that is still honest:
inferencebot). Own Unix user. Holds the co-op's mastercredentials (gateway admin, Cloudron, the room-administration account) and is the
fleet-administration path.
infrastructure-bot user), one Hermes profile per bot, and one gateway process serving all
of those profiles. This is where the resource savings come from.
credential, or any bot with member-data access, gets its own Unix user, because its
credentials must not be readable by its siblings.
The rule that decides the tier: a bot may be a profile only if we would be comfortable with
it holding every other credential in that tier. Profiles of one user share a home, so one bot can
read another's configuration and keys. That is acceptable when the scope is genuinely identical,
and unacceptable when it is not.
Why profiles for tier 2 (measured on the agent host)
process at ~0.3–0.75 GB of RAM.
of it disposable cache.
instead of needing N.
~2.4 GB free, which does not fit. Profiles are ~1–2 GB of disk and one process. On this host
profiles are close to necessary, not merely cheaper.
Per-bot composition (the permission backplane)
agent:<group>) with a model allowlistand a monthly cap, so spend is attributable and revocable per bot.
stewards, mention-required so it never speaks unprompted, isolated session lanes per steward,
and read-only digests for members.
staging app; destructive operations only in operator messages, never in a shared room.
primary; a compromise here exposes keys, never member records.
The load-bearing control: tool restriction
Credential separation between profiles only means something if a bot cannot read its siblings'
files. A profile with arbitrary shell or filesystem access can read every other profile's
secrets, and a single injected instruction then reaches the whole tier. Tier 2 therefore requires
an explicit toolset allowlist per bot (no arbitrary filesystem or shell access) and a
deliberately small tier. Where we cannot restrict tools for a given deployment, that bot belongs
in tier 3.
Adding a bot (repeatable, ~15 minutes once the pilot proves it)
profile → scoped gateway team and key → Matrix account and group room → per-bot configuration
(room, steward allowlist, toolset) → isolation test (confirm through the bot's own tools that it
cannot read a sibling's credentials) → hand it to the group with the norms in
docs#6.
Accepted trade-offs
the tier user exposes all tier-2 credentials. Accepted in exchange for the resource savings,
with the tier kept small and its credentials low-privilege.
identities and steward-only prompting. Anything needing a hard guarantee belongs in tier 3.
First step, before building
Verify profile behavior empirically with a throwaway profile: that profiles share the runtime,
that the tier's gateway serves them, that each profile's environment and credentials are its own,
and that toolsets can be restricted per profile. A configuration that looked correct and was not
cost us a cycle already (#17); these numbers should be measured, not assumed.
Open decisions
First step done (2026-10-26): profiles verified on the tier-2 user. The numbers hold, and one control turns out to be mandatory rather than nice-to-have.
[MULTIPLEX] … serving every profile from this process (no restart), laterProfile 'X' unserved), and it re-scans a profile when its config or.envchanges. So a new group bot — including its credentials — can be added without touching the gateway at all..envandconfig.yaml. Cloning deliberately leaves messaging tokens and allowlists behind, so a cloned bot has no Matrix credentials until its own are added — which is what we want anyway..env. Profile separation is credential organisation; the toolset allowlist is the control. Every group-bot profile must therefore carry an explicit read-only toolset — a provisioning step, not a nicety.Consequences for the build-out: