What an agent may find, read and publish
How a read-only context library keeps discovery, search, observation and publishing apart, with a capped catalog of untrusted text and access rechecked for each observer. This is a contract rather than a deployed service.

On this page
Most of what an agent needs to know about an organisation isn’t in its code. What a metric actually counts and how a team reviews a change are facts that move faster than any agent gets redeployed, which is why Cloudflare OS keeps them outside the agent’s definition, in a library of collections, documents and SKILL.md skills that agents consult at run time. I adopted that design for Ferrite, my constrained application platform, as a source package developed with coding agents under my direction.
A shared library looks like the easy case for permissions, since everyone is only reading. It gets harder once you list what reading has to cover. Say a tenant keeps its metric definitions and review practices in a public collection, visible across the tenant, and Alice keeps notes on a difficult customer escalation in a private collection of her own. Her agent should be able to find and use both while it works for her. It shouldn’t carry those notes into a session she later shares with Bob. If the description of some skill in the public collection tells it to ignore its instructions, it shouldn’t do that either, and nothing it reads should let it change what the next agent reads. Finding something, reading it, showing someone else what you read and publishing are separate operations with different ways of going wrong, and the package treats them separately.
What the package provides is a contract rather than a service: a set of access-control state machines with deterministic, offline gates, which no application serves over a route yet. This is the right stage to settle them, because these are expensive decisions to retrofit once a service is carrying real documents.
Finding
Discovery most needs a budget, because its output goes to a model that hasn’t decided anything yet. The package’s one dependency is a gatekeeper package, the capability layer also adapted from Cloudflare OS, and it leans on that rather than growing parallel machinery. Discovery goes through the gatekeeper’s hard-capped catalog: 25 entries, ids of up to 256 characters, descriptions of up to 400. That puts the worst case at about 16,000 characters however many skills a tenant publishes, and whatever doesn’t fit has to be found by searching for it.
The catalog is also classified as untrusted display data. A description was written by whoever published the skill and is about to be read by a model working for somebody else, which makes it an obvious place to plant an instruction. The body of a skill is written to instruct an agent, and reaching it takes a read that can be checked or a slash command that names it. A catalog line has no such standing. It’s a label to choose by. Classifying text doesn’t stop a model from believing it, so the protection that matters is that seeing an id in the catalog gets the agent nothing: the read that follows is checked as if the catalog had never mentioned it.
Reading
Search, list and read run over visible collections only, and the rule the package locks down is that a query never returns content the account can’t read. Search needs saying separately because it is a read that doesn’t look like one. A result line from Alice’s notes, returned to Bob, would have read those notes for him, whatever the read operation would have said had he asked it directly.
The index behind this is small on purpose, a deterministic keyword index that requires every token to match, and nobody should mistake it for a search engine. A deployed service would swap it for Ferrite’s search engine, and what has to survive the swap is the interface and the rule. Settling the rule against a toy index means it can be tested exactly, with no ranking or fuzziness to argue about, and the real engine then has a fixed requirement to meet. That requirement is harder than it looks, since ranking statistics, snippets and caches each give a private document ways to influence what another account sees, and a small AND-token index has none of them.
Visibility belongs to the collection, which is either public across the tenant or private to an account, and the package itself has very little authority to lose. It is composed into a consuming application’s source tree and runs under that application’s injected authority alone. Nothing in it reads a clock, a socket or a file: every timestamp is a parameter and every document arrives as an argument, which is also what lets the tests be deterministic.
Showing
What happens after the read is the part that’s easiest to forget. In the contract, a read from a collection on a surface other people can watch is described to the gatekeeper’s observation authorizer, and when Alice shares the session, access is verified again for each observer, with one access check per collection the session observed, against the library’s own visibility rules. If her agent only read the public collection, the check passes for anyone in the tenant, because sharing shows Bob nothing he couldn’t have read himself. If it read her notes, the check refuses Bob.
The read was legitimate when it happened, and that has no bearing on Bob, since Alice’s authority was only ever good for Alice. Checking per collection keeps the cost in proportion to what the session touched rather than to how many reads it made, and it can be that coarse because visibility is set at that level in the first place.
Publishing
For agents there is no publishing at all. The library is strictly read-only to them, and publishing stays on a human or deploy path. The surface an agent gets is search, list, read, the catalog and slash-command expansion, and none of those writes, so there is no permission for anyone to talk it out of. What that closes off is propagation. Agent memory is already a delayed injection channel, where text saved today is read next week by a model that can’t remember where it came from, and a shared library with a write path would be the same channel with a tenant-wide audience. An instruction planted on a web page an agent happened to read could become a skill that every other agent in the tenant later loads.
Two smaller decisions keep the same discipline. Frontmatter is validated by a parser written by hand for a key: value subset, and YAML constructs outside that subset are refused rather than guessed at. A constrained application couldn’t link a YAML crate anyway, so principle and necessity happened to agree, but the refusal would be right regardless, because a parser that guesses lets a file mean one thing to the person who published it and another to the agent reading it. And git-backed collections record their remote as provenance and nothing more. The package doesn’t fetch, clone or diff, and syncing is the host’s job under its own scoped egress, so the library can tell you where a skill came from without having any means of going there.
These gates only govern the paths that pass through them. A service still has to put a route in front of them and a real search engine behind them, and that work could undo all of this with one careless log line or shared cache. What it wouldn’t have to do is decide, in a hurry and under load, what an agent may see advertised, what an account may read, what an observer may be shown and who may change any of it.
Chris Chabot, November 2026