mehendocs

ConceptsPolicy

Policy#

What decides whether a request goes through?

On this page

A request that passed the binding meets the item's policy, which says allow, approve (wait for a human, see Approvals) or deny.

The shape#

type Policy = {
  default: "allow" | "approve" | "deny"
  rules: {
    action: "allow" | "approve" | "deny"
    methods?: string[]
    paths?: string[]
    body?: BodyMatcher
    note?: string
  }[]
}

Rules combine as deny over approve over allow, never first match wins, so adding a rule cannot silently open something. If no rule matches, default applies. A rule may name a body matcher for requests whose meaning is in the body rather than the path, such as GraphQL. Matchers fail closed: a body they cannot read in full (empty, not UTF-8, invalid JSON, a repeated JSON key, a batch) is treated as the risky case, so it is held or refused, never let through.

Policy, not intent, decides. Some reads and narrowly allowed writes run without asking, and a human may change a credential's policy from its page with a passkey. Requests in protected environments also need an approval (Projects, environments and runs).

What templates allow, hold and refuse#

Provider templates fail closed. For an item made from a template:

mehen templates <id> prints a template's rules as three lines (without a human, needs approval, refused), and mehen show <item> shows an item's policy. Providers and templates lists every template's three lines. Do not look for a way round a denied route; use the alternative:

You try toThe vaultDo this instead
Send a saved Gmail draft by id (drafts/send)Denies it (policy_denied)A draft can change after the send is approved, so the approval could not show what is sent. Send the whole message with POST .../messages/send and a raw body; the approval shows it.
Send a stored Microsoft Graph message by id, or forward or reply to a stored messageDenies itThe same reason. Send the whole message with sendMail.
Use Microsoft Graph $batchDenies itA batch carries requests the vault cannot check one by one. Send each request on its own.
Change mail forwarding, delegates, filters, mailbox rules or settings; add SSH or deploy keys, webhooks, collaborators or app registrationsDenies itThese give lasting access to someone else. Ask the human to do it themselves, outside the vault.
Mint or revoke an API token, invite a member, add a webhook, log drain or event destination, or delete a whole project or organisationDenies itThe same reason, and a deleted project does not come back. Ask the human to do it in the provider's own console.
Read a Fathom recording's summary or transcriptHolds it for approvalThat route can post the text to any URL named in its query, which no rule can see. List meetings with GET /meetings?include_transcript=true instead.
Call NotionNeeds a Notion-Version headerThe vault adds only the secret; send Notion-Version yourself on every request.
Reach a scoped npm package (@scope/name)Refuses it as an ambiguous pathIts name travels as @scope%2fname, an encoded slash. Unscoped packages work; ask the human to publish a scoped one.
Run a GitHub GraphQL requestAllows a read-only query that a strict matcher can read in full; holds anything else for approvalPrefer a plain query, or the REST endpoint.

Credentials without a template#

A credential stored without a template (an agent proposed its binding) fails closed as well: its policy is approve by default with no rules, so every request waits for a human. The intake page offers one tick, unticked, to let reads (GET, HEAD, OPTIONS) through without asking; the passkey signs that choice with the rest of the form. A rotation keeps the item's policy, and a template's intake takes the template's. Only a human can widen a policy afterwards, from the item's page with a passkey.

How the summaries are written#

The three policy lines (without a human, needs approval, refused) for a built-in template use the template's reviewed notes ("reads; creates a draft; changes labels"). For an item, they describe each rule from what it matches (method and paths), never from its note, because nothing proves an item's policy is a template's. "anything else" always lands on the line of the policy's default. mehen check runs the same matcher locally to say what a request would get; it is advisory (Find a credential and call an API).

Policy proposals#

mehen policy preview and propose exist in the command line, but the vault refuses them in this release with forbidden ("Policy widening is not available yet"). Do not retry them; a policy window never stands in for a protected context. mehen policy show, cancel and stop work.