Skill Authority Scoping: Giving Each Tool The Smallest Privilege It Can Function With
Most skills run with the agent's full credential set. They should run with capabilities scoped to the smallest task they need. The spec, the runtime work, and a manifest you can write today.
Continue the reading path
Topic hub
MCP SecurityThis page is routed through Armalo's metadata-defined mcp security hub rather than a loose category bucket.
Turn this trust model into a scored agent.
Start with a 14-day Pro trial, register a starter agent, and get a measurable score before you wire a production endpoint.
TL;DR
Most agent runtimes hand every imported skill the same credential bundle the agent itself holds. A read-only summarization skill ends up with write access to the same systems a financial-action skill needs, because nothing inside the runtime distinguishes between the two. The fix is the same one that worked for cloud IAM, mobile app permissions, and OAuth scopes: a per-skill capability manifest that the runtime enforces at the call boundary, with capabilities sized to the narrowest privilege the skill can actually function with. This post specifies the manifest, walks the runtime work needed to enforce it, identifies the failure modes, and ends with a Skill Capability Manifest Spec you can paste into any skill in your registry today.
Intro: The Skill That Should Have Been Read-Only
A reporting skill in a marketing agent's toolkit was supposed to do one thing: query a list of campaigns from an ads platform and return summary metrics. It needed read access to a single endpoint. The skill was authored cleanly and shipped with no obvious problems. Six weeks later, an entirely separate bug β a confused control flow in a different part of the agent's instructions β caused the reporting skill to be invoked with arguments that, when forwarded to the ads API, paused half the campaigns in the account.
The skill itself was not malicious. The skill author had not anticipated this argument shape. The agent's instructions had not been written to call the skill this way. The runtime did not know the skill was supposed to be read-only, because nothing in the skill's metadata said so. The credentials the skill used were the agent's full credentials, granted at runtime startup, scoped to whatever the agent's API key happened to allow. Read access and write access lived in the same bundle.
This is the modal failure of agent runtimes today. Skills are imported with the runtime's full authority. Authority comes from the agent's identity, which is bound to a credential bundle, which is usually scoped to whatever the operator's API key permits. The granularity stops at the agent. Inside the agent, every skill operates with the same authority as every other skill. The only thing standing between a read-intended skill and a write-capable misbehavior is the skill author's discipline and the agent instructions' precision. Both are unreliable.
The principle of least privilege has been the right answer to this in every other ecosystem. Cloud IAM gives each service identity the narrowest permission set it can function with. Mobile platforms ask each app for specific permissions. OAuth scopes let a third-party application request only the rights it actually needs. The agent runtime is the next ecosystem to adopt the discipline, and the work is mechanical: define a capability manifest format, require skills to declare their needs in it, enforce the declaration at the runtime call boundary, and refuse to load skills that declare more than they can justify.
This post is the specification for that work. The capability manifest is not new β every prior ecosystem has written a version of it β but the agent-runtime version has specific requirements that come from the skill model: per-call argument constraints, time-bounded grants, scope-narrowed credentials, and runtime-enforced refusal. Done right, it produces a runtime where a skill that asks for read can only read, a skill that asks for narrow write can only narrow-write, and a skill that asks for too much is simply not loaded. Done wrong, it produces compliance theater. The line between the two is in the enforcement layer, which is where most past attempts have collapsed.
Section 1: Why Per-Agent Authority Is The Wrong Granularity
The authority model in most agent runtimes today is per-agent. The agent has an identity. The identity has a credential. The credential is loaded into the runtime at startup or per-turn. Every skill the agent imports operates under that credential, with whatever privileges the credential grants on whatever downstream systems the credential addresses. The granularity of authority equals the granularity of identity, which is the agent itself.
This is wrong for the same reason that running every process on a Unix server as root is wrong. The skills inside an agent are independent units of code authored by independent parties with independent failure modes. They have no business sharing a credential bundle. The fact that they happen to be loaded into the same runtime process is an implementation detail that the authority model should not respect.
The consequence of per-agent authority is that the worst-privileged skill effectively defines the agent's exposure. If the agent imports thirty skills and one of them needs write access to a critical system, the agent's credential includes that write access, and the other twenty-nine skills inherit it whether they need it or not. A compromise of any of the twenty-nine skills can exercise the write capability that only one of them was supposed to use. The compromise does not have to be malicious authorship. A bug, an unintended argument path, a misuse of the skill from the agent's instructions β any of these can cross the privilege boundary that the runtime did not draw.
The right granularity is per-skill, and ideally per-skill-per-call. A skill should hold only the authority it needs for the operation it is currently performing. When the operation completes, the authority should be released. The runtime should be the holder of the agent's full authority, the skills should hold only narrow grants the runtime issues for specific operations, and the grants should be enforced at the call boundary where the skill makes its outbound requests.
This is structurally similar to how OAuth works between applications. The user holds the underlying authority. The third-party application requests a scoped subset. The authorization server issues a token that encodes the scope. The resource server enforces the scope on every request. The user, the application, the authorization server, and the resource server are four distinct roles, and authority moves between them in tightly defined ways. The agent runtime needs to play all four roles for its own internal skill ecosystem: it holds the authority, the skills request scopes, the runtime issues per-call grants, and the outbound call boundary enforces them.
The practical objection to per-skill authority is overhead. Every skill, every call, every grant, every enforcement check β surely this is too much machinery for the simple case of a small agent with a few skills? The answer is that the simple case is exactly where the discipline matters least and is therefore the worst place to debate it. The mature case β agents with dozens of skills, sourced from multiple parties, holding pacts that depend on specific failure modes not happening β is where the discipline is load-bearing. The runtime should support both, with the manifest format being the bridge.
Section 2: The Capability As The Unit Of Grant
A capability is the smallest grant of authority a skill can hold. It is a structured object, not a string. It identifies the resource the grant addresses, the operations it permits on that resource, the constraints that narrow those operations, and the lifetime of the grant. Every term is necessary. Skipping any of them produces a grant that is either too broad to enforce or too narrow to use.
The resource is what the capability points at. For an agent runtime, resources are typically external systems addressable by URL, API surface, or credential scope. A capability on a calendar API addresses a specific calendar, not all calendars on the account. A capability on a database addresses a specific table or query, not the whole schema. The resource identifier should be as specific as the underlying system supports β addressing whole accounts when the system supports addressing individual records is laziness that will be paid for later.
The operations are the verbs the capability permits. Read, write, delete, execute. The verbs should be drawn from a vocabulary the underlying system understands, not invented per skill. If the underlying API has a notion of "list" distinct from "get," the capability vocabulary should expose both. If the underlying API has a notion of "approve" distinct from "submit," the capability vocabulary should distinguish them. Mapping skill verbs to underlying system verbs is the first place the manifest can be wrong, and getting it right requires reading the system's actual documentation rather than the skill's documentation.
The constraints narrow the operations. A capability that permits write on a calendar might constrain writes to events created by the agent itself, prohibiting modification of events created by humans. A capability that permits read on a database might constrain reads to records belonging to a specific organization, prohibiting cross-tenant access. Constraints are where the manifest does most of its load-bearing work, because most security incidents are not about wrong verbs β they are about right verbs applied to wrong objects.
The lifetime is how long the capability is good for. Lifetimes can be wall-clock β a capability valid for one minute, ten minutes, one hour. They can be operational β a capability valid for one call, ten calls, one session. They can be scoped to a transaction β a capability valid until the agent's current turn completes, then released. The default should be the shortest viable lifetime, with longer lifetimes requiring justification in the manifest.
The four-term capability is the unit. A skill manifest is a list of capabilities the skill needs. A capability grant is the runtime's instantiation of a capability for a specific call, with the resource resolved, the operation permitted, the constraints encoded, and the lifetime started. The grant is what the skill holds during execution. The manifest is what the skill author commits to. The runtime is what enforces the gap.
This is a more disciplined model than what most current agent runtimes implement, which is closer to "the skill has the agent's credentials, do whatever you want." Moving from that to capability-based authority is not a small change. It changes how skills are authored, how the runtime authenticates outbound calls, how the agent's identity is represented, and how operators reason about exposure. It is also the only way to get to a place where an operator can say, with evidence, "this skill cannot do that."
Section 3: Read-Only Capabilities And Why They Are The Easy Win
The least controversial application of skill authority scoping is the read-only capability. A skill that reads should not be able to write. This is so obvious that the only reason it is not the universal default is the inertia of per-agent credential bundles. Fixing it is the easy win that gets the rest of the framework into operation.
A read-only capability is a capability whose operation list contains only verbs that retrieve data and have no observable side effects on the underlying system. The constraint is that no method, no parameter, no header on the outbound call may be one that the underlying system would treat as a write. This requires the runtime to know enough about the outbound API to recognize which verbs are which, which is a real piece of metadata work but not an unbounded one β the major API surfaces an agent uses are documented enough to enumerate.
The enforcement happens at the outbound call boundary. The skill is loaded with a capability that permits read on, say, a specific database table. The runtime intercepts the skill's outbound calls. The interception checks the call against the capability: is the call addressing the permitted resource, using a permitted verb, satisfying the constraints, within the lifetime. If yes, the call proceeds and is logged as a verified-permitted call. If no, the call is refused, and the skill receives a clean error indicating the capability was insufficient.
The interesting case is the skill that needs to fall back. A read-only skill that encounters a required follow-up action β say, a summarization skill that wants to file a follow-up note about a record that needs further processing β has options. It can return the follow-up to the agent and let the agent decide whether to issue the action. It can request a capability upgrade from the runtime, which prompts the agent and possibly the operator to approve the broader grant. It can fail cleanly and let the agent route the request to a different, more-privileged skill. Each path keeps the read-only skill read-only.
The operational practice that follows from read-only capabilities is to default skills to read-only and require explicit justification for any write capability. The manifest format should make this the path of least resistance: declaring read access is a one-liner, declaring write access requires specifying the resource, the verbs, the constraints, and a justification. The friction is intentional. It is the same friction that mobile app stores impose on apps that request broad permissions: not impossible, but visible enough that the request gets reviewed.
The quantitative payoff is large. In a fleet where most skills can be read-only, scoping them to read-only eliminates most write-capable misbehavior paths. The remaining write-capable skills are a small set, and they are the ones that deserve the most authoring attention, the most review, and the most monitoring. The fleet's write-capable surface area shrinks to the surface that actually needs to be write-capable, instead of being the union of every credential the agent happens to hold.
Section 4: Write-Scoped Capabilities And The Constraint Problem
A write capability is harder than a read capability because the constraints have to do real work. "Write to the calendar" is too broad. "Write a single event with these properties to this calendar" is closer. The constraint problem is the question of how narrowly a write capability can be expressed without becoming impossible to author and impossible to enforce.
The right framing is per-call constraint expressiveness. The manifest declares the shape of the writes the skill will perform, in terms specific enough that the runtime can enforce them. "This skill writes to the events resource of the calendar API, and the events it writes will have the following properties: organizer matches the agent's identity, attendees are within an approved domain, duration does not exceed two hours, recurring rules are not used." This is a rich constraint, and a runtime that enforces it can refuse a call that, for any reason, tries to write outside the declared shape.
The constraint syntax has to support the kinds of properties that real APIs care about. Equality on field values. Membership in a set. Pattern matching on strings. Numeric bounds. Combinations under conjunction and disjunction. A constraint language that supports these is expressive enough for most cases without becoming a Turing-complete configuration nightmare. The manifest should declare constraints in a structured form that is checkable in the runtime without arbitrary code execution.
The payload-shape constraint is the most important kind. A write capability that permits writes whose payload matches a declared schema β JSON Schema, structural typing, whatever the runtime supports β is enforceable. The runtime checks every outbound call's payload against the schema and refuses any call whose payload does not match. This is mechanical and does not require the runtime to understand the API's semantics; it only needs to compare the payload to the declared shape.
The identity-match constraint is the second most important. A write capability that permits writes only when the payload's actor field matches the agent's identity catches a class of impersonation bugs that would otherwise propagate. The runtime knows the agent's identity. The constraint declares that the skill's outbound payloads must reference that identity in specific fields. Mismatches refuse.
The time and rate constraints are useful as a third tier. A capability that permits writes at a rate of no more than ten per minute, or only between business hours, or only during the agent's currently-active turn, narrows the misbehavior window. A skill that compromises and tries to issue a thousand writes in a second simply cannot, because the capability's rate constraint refuses past the limit. The constraints are not a substitute for monitoring, but they shrink the damage a single compromised skill can do before monitoring catches up.
The difficulty of write-scoped capabilities is that they require the manifest author β usually the skill author β to think clearly about the shape of the operations the skill will perform. Sloppy manifests produce constraints that are too broad to be useful or too narrow to be honored. The discipline of writing tight constraints is itself a forcing function that makes skill authors think about their skills' authority profile, which is a benefit of the model independent of the runtime enforcement.
Section 5: Time-Bounded Capabilities And Transient Access
The lifetime of a capability is the third axis of scoping, and it is the one most often left at "forever." A capability granted at skill load and held until skill unload is a capability whose enforcement window is the entire agent lifecycle. This is rarely what the skill actually needs. Most skills need authority for the duration of a specific call or a specific transaction, and holding it longer is dead weight that increases the exposure window without adding utility.
Time-bounded capabilities make the lifetime explicit. The simplest version is wall-clock: the capability is valid until a specific timestamp. After that, the runtime treats it as expired and refuses any call attempting to exercise it. This is useful for capabilities that should be transient by nature β a capability granted to perform a single approval, valid for ninety seconds, expiring whether or not the approval was performed.
The per-call version is tighter. The capability is valid for exactly one call. The runtime issues the capability, the skill exercises it on its next outbound call, the runtime marks it consumed, and any subsequent call requires a fresh capability. This is the right shape for high-stakes operations: each financial transfer, each data deletion, each administrative action gets its own capability, and the capability does not survive its use.
The per-transaction version sits in between. A capability is valid for the duration of a logical transaction β the agent's current turn, a specific user-initiated workflow, a defined unit of work β and is released when the transaction completes. This is useful for capabilities that need to be exercised multiple times within a coherent operation but should not survive past it. A capability to read a customer's records during a support session is needed for the duration of the session and not after.
Lifetime enforcement requires the runtime to track capability state, which is a real piece of bookkeeping but not an unbounded one. The capability registry holds, for each issued capability, the issuance time, the expiration time, the consumption count, and the transaction binding. The outbound call boundary checks the registry on every call. Expired or consumed capabilities are refused. The bookkeeping is small per agent and amortizes well across the runtime.
The operational practice is to default capability lifetimes to short. Long lifetimes should require justification in the manifest. "This skill needs the capability for the duration of the agent's process" is a valid declaration but should be rare; "this skill needs the capability for sixty seconds during the approval workflow" should be common. The friction toward shorter lifetimes is the thing that produces the audit-friendly behavior.
The interaction between time-bounded capabilities and skill authoring is subtle. A skill written assuming long-lived authority will misbehave when given short-lived authority β its caches go stale, its handles go invalid, its retries fail. Authoring skills against time-bounded capabilities requires the skill to be aware of capability expiration and to request renewal at the right points. This is a discipline, but it produces skills that are inherently more careful about authority management, which is its own benefit.
Section 6: The Manifest Format And Its Lifecycle
The capability manifest is the artifact that ties the authority model together. It is a structured document, attached to the skill, that declares the capabilities the skill needs, the constraints those capabilities should carry, and the lifetimes the runtime should issue them with. The format must be machine-readable, version-controlled, signed, and verifiable. Anything less and the manifest becomes a documentation file that the runtime ignores.
The top of the manifest identifies the skill: name, version, content hash, signing identity. These fields tie the manifest to a specific artifact and enable the runtime to refuse a manifest that does not match the skill it is being asked to govern. A manifest from an old version of a skill should not govern a new version, even if both share the name; capabilities can change between versions, and inheriting an old manifest into a new version's runtime is a path to inappropriate authority.
The body of the manifest is the capability list. Each capability has the four required fields β resource, operations, constraints, lifetime β plus an optional justification field where the author explains why the capability is needed. The justification is not enforced by the runtime, but it is read by operators during manifest review and is part of what makes the manifest auditable as a human document, not just a machine input. Manifests with weak justifications or boilerplate language signal skills that have not thought hard about their own authority, which is itself useful information.
The foot of the manifest is the signature. The manifest, like the skill artifact itself, must be signed by the skill author with a verifiable identity. The signature ties the capability declarations to the same identity that produced the artifact, so that a runtime checking the manifest is checking a claim that the responsible author has made. Manifests without signatures are anonymous claims and should be refused by any runtime that takes capability scoping seriously.
The lifecycle of the manifest mirrors the lifecycle of the skill. New manifest with each new version. Manifest changes β even without artifact changes β are themselves events that should require operator review, because a change in declared capabilities is a change in the authority footprint regardless of whether the executable bytes changed. A skill that quietly broadens its capabilities between versions is doing the agent-runtime equivalent of an app store request expansion: it should be a visible event, not a silent one.
Distribution of the manifest happens alongside the artifact. The manifest is published to the registry with the artifact, fetched together, verified together, and refused together if either piece fails. Treating the manifest as a sidecar that travels with the artifact through every step of the supply chain β registry storage, runtime fetch, cache, import β keeps the two synchronized and makes drift detectable.
Review of the manifest is the part that requires human attention. New skills, on first import to an environment, should have their manifests reviewed by an operator. Capability-changing version updates should require re-review. Manifests that cross thresholds β a previously read-only skill requesting write access, a previously narrow capability widening to a broader resource β should trigger explicit re-review even if a routine update would have been auto-approved. The runtime should make these review prompts unavoidable rather than dismissable.
Section 7: Runtime Enforcement And The Outbound Call Boundary
Manifests without enforcement are documentation. The enforcement layer is what makes the manifest a real boundary, and it lives at the place where the skill makes outbound calls. Whatever the runtime's mechanism for outbound calls β HTTP client, MCP tool dispatcher, native function calls β that mechanism becomes the choke point where capabilities are checked.
The enforcement loop on every outbound call is: identify the calling skill, look up its current capability set, identify the resource and operation the call is targeting, check the call against each capability in the set, refuse the call if no capability covers it, issue and log the call if a capability does. The loop runs on every call, with no exceptions, because exceptions are exactly what an attacker would exploit. The cost of the loop is small β a few hashmap lookups and a constraint check β and is dwarfed by the cost of the underlying outbound call itself.
The identification of the calling skill is the first non-trivial piece. The runtime needs to know, on every outbound call, which skill is currently in control. This requires the call stack to carry the skill identity through whatever execution model the runtime uses. In single-threaded runtimes this is straightforward. In multi-threaded or async runtimes it requires explicit propagation through context-passing, async-local storage, or whatever mechanism the runtime uses for thread-local state. Getting this right is the most error-prone part of the enforcement layer, because a runtime that loses track of which skill is in control will issue capabilities to the wrong skill.
The constraint check is the second non-trivial piece. A capability that constrains payload shape requires the runtime to inspect the outbound payload, parse it into a structured form, and compare it against the declared schema. This is feasible for structured payload formats β JSON, protobufs, structured form data β but harder for opaque ones β raw byte strings, custom binary formats. The runtime's enforcement coverage is bounded by what it can inspect. Skills that emit opaque payloads should be capability-restricted to verbs whose semantics do not depend on payload contents.
The refusal path matters as much as the success path. When a call is refused, the runtime should return a clean structured error to the skill, identifying the capability that was insufficient and the call attribute that violated it. Skills should be able to handle these errors as first-class outcomes, distinguishing them from network errors or system unavailability. A well-authored skill that hits a capability refusal can fall back to a permitted alternative, request a capability upgrade through the agent's mediation, or fail to the agent with a clear reason. Skills that crash on capability refusals are skills that do not respect the authority model.
The logging path is the audit trail. Every call β permitted or refused β should produce a log entry with the skill identity, the capability that governed the call, the outbound resource and operation, the call outcome, and the timestamp. This produces a high-volume but high-value stream that lets operators answer questions like "what did this skill do this week, and was any of it surprising," "how often did capability refusals fire, and which skills are bumping into their boundaries," and "are there capability declarations that have not been exercised in months and could be removed." The audit trail is also what the trust score reads to confirm that the agent's authority discipline is being honored in practice, not just on paper.
Section 8: The Failure Modes Of Capability Models
Capability scoping is not free of failure modes. It has its own ways of being wrong, and a serious adoption requires knowing them.
The first failure mode is over-broad declarations. A skill author who does not want to think hard declares the broadest capability the runtime will permit. "Read everything, write everything, lifetime forever, no constraints." The runtime accepts the declaration and enforces it, which is to say it enforces nothing useful, because the declaration is the union of all possible operations. The fix is operator review of manifests, with bias against broad declarations and a default of refusing to load skills whose manifests look like rubber stamps. The discipline lives in the review process, not in the runtime alone.
The second failure mode is under-declared capabilities that produce constant runtime refusals. A skill author who declares too narrowly produces a skill that does not work β every other call is refused because the capability did not anticipate the actual operation. The fix is iteration: skills should be testable in a mode where the runtime logs what capabilities the skill would have needed, and the manifest author refines the declaration based on the observed needs. Without this iteration, capability declarations are written from first principles and miss the actual usage patterns.
The third failure mode is constraint expressiveness gaps. The constraint language supports patterns the underlying API does not, or vice versa. A constraint that says "only writes whose target field equals X" is meaningless if the underlying API does not expose a target field with that semantic. A constraint the operator wants to express but the language cannot represent leaves a hole in the enforcement. The fix is to keep the constraint language closely aligned with the actual semantics of the most-used outbound APIs, and to extend the language as new patterns become important. The language is a living artifact, not a one-time spec.
The fourth failure mode is lifetime mismanagement. Capabilities issued with overly-long lifetimes accumulate in the runtime as effectively-permanent grants. Capabilities issued with too-short lifetimes produce constant refresh storms that defeat the purpose. The fix is conservative defaults with clear semantics: the default lifetime is short enough to be safe, longer lifetimes require manifest justification, and the runtime exposes lifetime statistics so operators can tune the defaults based on observed patterns.
The fifth failure mode is skill authors gaming the manifest. A determined adversarial author can declare a capability that looks narrow but, through clever use of the constraint language or the resource identifier, actually grants broad authority. The fix is review combined with testing: manifests should be inspected, and the runtime should support adversarial test modes that try to exercise capabilities at their declared boundaries. Skills that can be made to do too much within their declared capability set are skills whose manifests need tightening.
The sixth failure mode is the social one: the operator who reflexively approves every manifest because reviewing them is tedious. This is the same failure mode that mobile app permission prompts produced for years, and the same fix applies: make the consequences of approval visible, batch reviews to amortize the attention cost, and prioritize review effort against the blast radius of the skills involved. High-blast-radius skills get more attention; low-blast-radius skills get a lighter touch.
Section 9: The Reader's Artifact β The Skill Capability Manifest Spec
This is the deliverable. Use this format to author manifests for new skills and to retrofit existing ones. The format is a structured object with five required fields and one optional one. Each field has a precise interpretation and a precise enforcement rule.
The first field is skill: an object identifying the skill the manifest governs. Required sub-fields are name, version, contentHash, and signingIdentity. The runtime must verify all four match the loaded artifact before honoring the manifest.
The second field is capabilities: an array of capability objects, each with the four required sub-fields. Each capability declares one resource-and-operation combination the skill is authorized to perform. The array represents the full authority set the skill will hold; capabilities not listed are not granted.
The third field is the per-capability resource: a structured identifier for the resource the capability addresses. The structure depends on the resource type β URL for HTTP, table-and-organization for databases, scope-and-account for OAuth-protected APIs. The identifier should be as specific as the underlying system supports. Wildcard identifiers are permitted but should be flagged for review.
The fourth field is the per-capability operations: an array of verbs the capability permits on the resource. The verbs come from a runtime-defined vocabulary that maps to the underlying system's operations. For HTTP-style resources, the vocabulary includes GET, POST, PUT, PATCH, DELETE. For database resources, it includes SELECT, INSERT, UPDATE, DELETE. For OAuth resources, it includes the relevant scope names.
The fifth field is the per-capability constraints: an array of structured constraint objects. Each constraint narrows the operations in some way. Supported constraint types include payloadSchema (declares the structural shape of permitted payloads), fieldEquals (requires a specified field to equal a specified value or match an allowed set), fieldMatches (requires a specified field to match a regex pattern), rateLimit (caps the rate of operations), actorBinding (requires the operation's payload to reference the agent's own identity in specified fields), and wallClockWindow (restricts operations to specific time windows).
The sixth field is the per-capability lifetime: an object describing how long the capability is valid. Sub-fields include kind (call, transaction, wallClock, process), value (a number or duration depending on kind), and renewable (a boolean indicating whether the runtime may auto-renew expired capabilities or must require explicit re-grant). Defaults should bias toward the shortest viable lifetime.
The optional field is the per-capability justification: a free-text string explaining why the capability is needed. The runtime does not enforce the justification but operators read it during review. Manifests with empty or boilerplate justifications should be flagged.
A worked example for a read-only summarization skill: skill name crm-summarizer, version 1.4.0, capabilities array containing one capability with resource pointing at the CRM's records endpoint scoped to a specific organization, operations limited to GET and LIST, constraints requiring the organization field to equal the agent's current organization context, lifetime of kind transaction bound to the current agent turn, and a justification field describing the read-only nature of the workflow.
A worked example for a narrow-write skill: skill name meeting-scheduler, version 2.1.0, capabilities array containing one capability with resource pointing at the calendar API's events endpoint scoped to a specific calendar, operations limited to POST, constraints including a payload schema that declares the event structure the skill will produce, an actor binding requiring the organizer field to equal the agent's identity, a rate limit of ten events per minute, and a wall-clock window restricting operations to business hours. The lifetime is call β each event creation gets its own capability.
Use the spec for every skill you author or adopt. Resist the temptation to declare broad capabilities for convenience. The cost of writing a tight manifest is paid once. The cost of running with broad capabilities is paid every time something goes wrong, and every time has a non-zero probability of going badly.
Section 10: A Counter-Argument Worth Taking Seriously
The principled objection to capability-based scoping is that it imposes coordination overhead on every party in the skill ecosystem in exchange for safety properties that, in practice, can be achieved with less invasive controls. Skill authors have to write manifests. Runtimes have to enforce them. Operators have to review them. The whole system has to agree on a constraint language and keep it current with the underlying APIs. The total bill is large; the question is whether the safety properties justify it.
The alternative posture is to invest the same effort in observability and reactive controls. Watch what skills do, alert on anomalies, kill skills that misbehave. The argument is that this catches the same failure modes with lower upfront friction, because the controls live in monitoring rather than in authoring. The skill author writes their skill, the runtime watches it, and only when something goes wrong does the operator have to act.
This argument is not wrong, but it is incomplete. Reactive controls catch failures after they have happened. Capability-based controls prevent them. For some classes of failure, reactive controls are good enough β the damage from a single misbehaving call is small enough that catching it after the fact is acceptable. For other classes, the damage is irreversible by the time the alert fires. The right model is layered: capability scoping prevents the worst-class failures from being possible at all, and reactive monitoring catches the residual classes that capabilities cannot fully constrain.
A second objection is that the capability model creates false confidence. An operator who believes their skills are scoped may underinvest in the monitoring that would catch the cases where the scoping was insufficient. The capability declarations might not capture every misbehavior path, and the operator may not realize this. The model lulls the operator into thinking the problem is solved when it is only partially constrained.
This is a real risk and the answer is humility about what the manifest covers. Capability declarations bound certain failure modes β wrong verbs, wrong resources, wrong payload shapes, wrong lifetimes β and leave others β semantically wrong but structurally valid behavior β to other layers. Operators should not stop at capabilities; they should treat capabilities as one layer in a defense-in-depth strategy that also includes provenance, sandboxing, behavioral evaluation, and runtime monitoring. The model is a floor, not a ceiling.
A third objection is that the constraint language will inevitably grow into a complex artifact that is itself a source of bugs. Every expressive control language eventually has corner cases, ambiguities, and unintended interactions. The capability constraint language will have these. Skills that exploit subtle gaps in the constraint language can pass capability checks while doing things the manifest author did not intend.
This is correct and is the reason the constraint language must be reviewed and extended cautiously. New constraint types should require explicit consideration of how they interact with existing types. The language should be small enough to reason about and maintainable enough to evolve. The trap to avoid is letting the language grow into a Turing-complete configuration system that nobody can audit. Keeping it boring is part of keeping it safe.
What Armalo Does About This
Armalo's skill registry stores a capability manifest alongside every skill artifact. The manifest format follows the spec above: structured capabilities with resource, operations, constraints, and lifetime, signed by the skill author. The runtime fetches the manifest with the artifact, verifies the signature against the same identity that signed the artifact, and refuses to load skills whose manifests fail verification or whose declared capabilities exceed what the agent's pact permits.
The runtime's outbound call boundary intercepts every skill call against the capability set the manifest declares. Calls outside the declared capabilities are refused with a structured error the skill can handle. Capability lifetimes are tracked per-skill in a registry that defaults to the shortest viable lifetime per call kind. The audit trail of capability checks β both permitted and refused β flows into the agent's per-turn forensic record and feeds the security and reliability dimensions of the composite score.
Manifests for new skills require operator review on first import. Manifests whose capability declarations broaden between versions trigger explicit re-review even when the artifact has otherwise been auto-approved. The runtime exposes capability-exercise statistics so operators can identify declared capabilities that have never been used and can be removed, and capability refusals that fire frequently and signal either author error or attempted misuse. The whole loop closes: the skill declares, the runtime enforces, the audit observes, the score reflects, the operator reviews.
Frequently Asked Questions
Why not just enforce at the API key layer? API key scoping operates at the granularity of the agent identity. It cannot distinguish between skills inside the same agent. A skill that needs only read should not be able to write, even though both operations are within the agent's overall permission. Per-skill capability scoping is the only model that draws the right boundary.
What about skills that need to call other skills? Skill-to-skill calls flow through the runtime, which enforces capabilities on each. A skill calling another skill does not transfer its capabilities; the called skill operates under its own manifest. This prevents a permitted skill from being used as a privilege-escalation channel by a less-permitted skill.
How do I retrofit an existing skill to a manifest? Run the skill in a mode where the runtime logs what capabilities it would need rather than enforcing them. Collect a representative sample of operations. Translate the observed operations into capability declarations, biasing toward the narrowest constraints that cover the observed shapes. Then enable enforcement and watch for refusals; refusals indicate either too-narrow declarations or unanticipated operations the skill should not be performing.
Does this work for skills that wrap many APIs? Yes. The manifest can declare multiple capabilities, one per resource the skill addresses. Each capability is enforced independently. Skills that wrap many APIs end up with longer manifests, which is appropriate to their broader authority footprint.
What about emergency overrides? The runtime should support operator-initiated capability grants for unusual circumstances, but they should be conspicuous: time-limited, audit-logged, signed by the operator's identity, and surfaced in the next score update. Emergencies that require capability overrides are signals that either the manifest was wrong or the situation was unanticipated, and both deserve post-incident review.
How does this interact with sandbox modes? Sandboxing constrains what the skill can do at the runtime layer β process isolation, container boundaries, microVM separation. Capabilities constrain what the skill can do at the authority layer β which resources, which operations, which payloads. The two are complementary: sandboxing limits the blast radius of code-level compromise, capabilities limit the blast radius of authority-level misuse.
Can capabilities be revoked at runtime? Yes, and they should be. The runtime should support immediate capability revocation for any skill, as a safety lever the operator can pull when something looks wrong. Revocation should be the default response to a signing identity change, an attestation failure, or a behavioral anomaly that exceeds threshold.
Do counterparties see capability declarations? Through the trust oracle, yes. A counterparty who queries an agent's pact compliance can see, at a summary level, the capability discipline the agent's runtime is enforcing. This becomes part of the trust signal: agents that operate under tight capability scoping have a stronger basis for their security and reliability claims than agents that do not.
Bottom Line
The per-agent credential bundle was a convenient way to bootstrap the skill ecosystem and is now the bottleneck for everything that comes next. Per-skill capability scoping draws the right boundary, with manifests that authors commit to, runtimes that enforce them, and audit trails that prove the enforcement happened. The Skill Capability Manifest Spec is short. The runtime work is mechanical. The discipline is what matters. Authors who write tight manifests and runtimes that enforce them produce agents whose security and reliability claims are checkable. Authors who declare broad capabilities and runtimes that wave them through produce documentation. Pick the first.
The Trust Score Readiness Checklist
A 30-point checklist for getting an agent from prototype to a defensible trust score. No fluff.
- 12-dimension scoring readiness β what you need before evals run
- Common reasons agents score under 70 (and how to fix them)
- A reusable pact template you can fork
- Pre-launch audit sheet you can hand to your security team
Turn this trust model into a scored agent.
Start with a 14-day Pro trial, register a starter agent, and get a measurable score before you wire a production endpoint.
Put the trust layer to work
Explore the docs, register an agent, or start shaping a pact that turns these trust ideas into production evidence.
Comments
Loading commentsβ¦