Two different things get called "scope", they are enforced in different places, and confusing them is how people end up granting more than they meant to.
| Where it lives | Changing it | |
|---|---|---|
| What the vendor granted | the vendor's consent screen | widening needs a browser, narrowing needs a reconnect |
| What your agent may spend | your policy file, on your machine | instant, both directions |
The first is a ceiling. The second is where you actually work.
Which scopes a given provider asks for, and which it deliberately does not, is on that provider's own page. Every provider has one.
What your agent may spend
Connecting grants a read bundle. Everything after that is policy, and policy is local:
$ lanes link policy list --profile personal
$ lanes link policy deny gmail.send --profile personal --workspace local
$ lanes link policy allow 'drive.*' --profile personal --workspace localRules are glob patterns over capability names. Two properties matter:
A deny always beats an allow, whatever the order in the file. There is no precedence puzzle to reason about, and no way to accidentally re-grant something by adding a rule below.
An absent policy grants nothing. The default is deny, so a capability nobody wrote a rule for is
not reachable. connect writes one rule per provider, which is why a fresh connection works at all.
A rule can also expire, with expires_at, for access you want to hand out for an afternoon.
Bundles
A bundle is a named group of capabilities: read and write are the two you will see. For providers
built on an HTTP specification they are derived from the method, so GET and HEAD land in read
and the mutating verbs land in write, with nobody curating a list that could quietly drift from
what the API does.
Granting one is how you widen without naming twenty capabilities:
$ lanes link connect gmail --add write --profile personal --workspace localWidening what the vendor granted
This is the one that cannot happen locally. If the vendor never granted it, no policy rule conjures it, and the endpoint has nothing to spend.
$ lanes link connect <connection> --add <bundle> --profile personal --workspace localThat reopens the browser, because a wider grant is the vendor's decision and yours, not ours. Some
scopes are marked broad, and connect stops and asks for an explicit yes before requesting one
rather than sliding it in with the rest.
Narrowing what the vendor granted
Reduce the bundles and reconnect. A vendor with a revocation endpoint has the old grant revoked as part of it; where there is none, the grant stays live at the vendor until you remove the app in their account settings, and the page for that provider says so.
Denying the capability locally is the faster half of this and takes effect at once. Do both when the point is to remove access rather than to stop using it.
Scopes are not set per connection
Worth stating plainly, because the shape of the CLI suggests otherwise.
Policy rules name capabilities, never connections. Every account of a provider inside one profile
is governed identically. If you have two Gmail connections in personal, denying gmail.send denies
it for both. There is no rule that reaches one and not the other.
That is deliberate rather than missing. A rule that named a connection would be a rule whose meaning changed when you relabelled one, and policy that silently re-points is worse than policy that cannot express something.
Granularity comes from profiles. A profile is the unit of separation, so the way to give one agent a narrower view is to give it a narrower profile:
$ lanes link profile add reading --profile-default false
$ lanes link connect gmail --profile reading --workspace local
$ lanes link policy deny 'gmail.send' 'gmail.users.messages.trash' --profile reading --workspace localRegister that profile with the agent you want held back, and the wider one with the agent you trust.
Nothing in reading can see what personal holds, which is the same isolation the
built-ins get.
Seeing what an agent actually has
$ lanes link status --profile personal --workspace local
$ lanes link tools --profile personal --workspace localstatus lists every connection and what it makes reachable. tools is the exact list an agent would
be handed right now. If the count your client shows disagrees with tools, your client is stale and
wants reconnecting, not repolicying.
Everything is recorded either way
A refused call is an audit entry, not a silent no-op. lanes link audit tail --denied-only is how you
find out that a policy is narrower than the work needs, or that an agent is reaching somewhere you did
not expect. See the audit log.
Next: every provider for the scopes each one asks for, or Capabilities for why a capability is a tool, a resource, or a prompt.