Skip to Content
PoliciesRead/Write Governance

Named reference journeys (GitHub, files/storage, email, Slack, customer data, cloud and database) have evidence only at their recorded scope; live vendor execution is not certified. This does not change enforcement of exact requests through enrolled managed MCP :call. See Scope and Limits.

Read/Write Governance

Read/write governance lets a project decide which governed agent actions may proceed, which require human review, and which are denied. The control is a project policy over Keel-derived action facts: access level, risk tags, connector identity, classifier source, and registry version.

For governed routes, Keel records those facts on the permit/audit metadata. On supported plans, decision snapshots, signed exports, and Keel’s tamper-evident integrity stack can then show what Keel classified, which policy ran, and what decision Keel returned for the actions that routed through Keel. That is the proof boundary; it is not a claim that Keel governs every action an agent can take off path.

What Keel governs

Keel governs actions that route through a Keel decision boundary:

SurfaceWhat Keel can governBoundary
Model calls through Keel execution routesPolicy and budget before provider dispatchModel operations are usually read on the access ladder, but billable calls can also carry the spend risk tag.
MCP tools routed through KeelExact managed MCP :call tool calls after Keel resolves a verified connector identity and tool nameOnly connector identities Keel has enrolled (an operator step) can reach a policy allow. Exact mapped names classify as read, write, or admin; unmapped names classify as unknown and are refused. A server you register yourself is held for review on every call; a human-approved action mapping can classify its tools, but their calls stay review-only.
Payments routed through KeelSpend-bearing payment actionsPayment and positive-cost actions carry spend.
Code execution routed through KeelCode-execution operations and known code-exec toolsClassified as admin with code_exec.
Credential-scoped databasesDatabase access through Keel-verified read-only or read-write credentialsThe downstream credential scope is the control; Keel does not parse SQL text to decide read versus write.

Keel does not govern direct calls the agent makes with its own provider, API, browser, database, or tool credentials. Browser and computer-use actions are coarse unless a concrete action is mediated by a governed Keel route or tool. On permit-first integrations, Keel records the decision before execution, but the caller still owns the downstream call and must honor the decision.

What Keel classifies

When action access is enabled for a project, governed policy context can include:

FieldMeaning
context._keel.action_access_levelOne of none, read, write, admin, or unknown.
context._keel.action_risk_tagsIndependent risk tags: spend, egress_external, data_export, code_exec, refund, pii_access, and value_movement.
context._keel.action_access_sourceWhere the classification came from, such as keel_curated_connector_map, keel_trusted_credential_scope, operation_intrinsic, or none.
context._keel.action_access_confidenceClassification confidence. A read classification requires high confidence.
context._keel.action_access_map_versionThe classifier registry version used for mapped connector or credential-scope classifications. Read the version from the decision record; this page does not pin one.
context._keel.action_access_registry_hashHash of the active classifier registry manifest.
context._keel.action_access_unknown_reasonWhy Keel could not classify the action, when the level is unknown.
context._keel.connector_identityThe Keel-verified connector identity. Keel does not trust a customer-renamable MCP server slug for read/write gates.
context._keel.intent_mismatchTripwire only: true when the caller declared less access than Keel derived, or Keel could not confirm the declared access.

Caller-supplied context._keel values are stripped before evaluation. A caller can declare intent, but declared intent never grants access.

Access levels

Access level is one ordered ladder. unknown is not a rung; it is the fail-closed sentinel.

LevelMeaningExamples in the live registry
noneNo action access beyond the permit decision itself.Used as an allowable read-only level; most concrete tools classify as read, write, admin, or unknown.
readRetrieves existing state without mutating the downstream system.GitHub get_file_contents; database query through postgres.readonly; Stripe get_payment_intent; filesystem read_text_file; Slack conversations.info.
writeMutates content, resource state, workflow state, or money movement.GitHub create_issue; database query through postgres.readwrite; Stripe create_payment; filesystem write_file; Slack chat.postMessage.
adminChanges privileged control state or runs a high-authority operation.GitHub merge_pull_request or create_repository; Google Drive share_file; S3 PutBucketPolicy; Slack conversations.invite; code execution run_command. The current registry does not expose an admin bucket for credential-scoped database or Stripe tools.
unknownKeel did not classify the action at high confidence.Unmapped MCP tools, unverified connector identities, unscoped database query, and generic common-MCP names such as read, list, get, or search.

Risk tags

Risk tags are independent of access level. A read-only policy should check both the ladder and the tag set.

TagMeaningExamples in the live registry
spendThe action spends, reserves, refunds, transfers, or can incur billable cost.Stripe create_payment, stripe_api_write, create_refund; payment action payment.execute; model calls with positive estimated cost.
egress_externalThe action sends data outside the expected trust boundary.Slack chat.postMessage; GitHub write/admin tools such as create_issue; Google Drive share_file; Stripe send_stripe_mcp_feedback.
data_exportThe action exports or exposes data as an explicit export/share/report operation.Google Drive share_file; S3 bucket-policy/public-access admin tools; Stripe stripe_report.
code_execThe action executes code.Operation code_execution; code-execution tools such as run_command, bash, python, and node.
value_movementThe action moves money or other value.Payment action payment.execute.
refundThe action returns money to a payer.—
pii_accessThe action reads or writes personal data.—

Fail-closed behavior

Read/write governance is intentionally conservative:

  • unmapped or unknown tools classify as unknown
  • unknown is not read-only safe and should review or deny
  • policy authoring rejects active policies that allow unclassified actions
  • low- or medium-confidence classifications do not become read
  • generic MCP verb names such as read, list, get, or search are not trusted as read semantics by themselves
  • database read/write classification comes from the credential scope Keel provisioned or verified, not from SQL parsing
  • any risk tag, including a future tag, blocks read-only-safe classification

Keel refuses unknown and unmapped tools at its platform safety floor (platform_safety_floor.unknown_action). No project policy can allow them.

Enable read-only mode for a project

For eligible hosted projects, read/write controls are generally available. They do not change behavior until the project has an active policy that uses context._keel.action_access_* fields.

In the dashboard, Guided Read only adds a restriction rule. It does not create positive authority: add an independently bounded positive grant where appropriate, or use Technical mode to author the explicit policy below. Save the policy as the active project policy. This requires Production or Enterprise: Starter’s Basic authoring cannot use allow, the exists operator, or the context._keel.action_access_* fields, so Starter cannot express a read-only policy.

Read-only means:

  • explicitly allow action_access_level values none and read — Keel executes only what a matching allow rule authorizes, so a policy with no allow rule denies every request, reads included
  • deny write and admin
  • give no positive grant to unknown, missing classification, or non-empty action_risk_tags; restriction rules may additionally record REVIEW

For harmless classified reads, this Technical-mode example supplies explicit positive authority. It is not the output of the Guided read-only compiler:

{ "name": "Action access: read only", "rules": [ {"if": {"not": {"field": "context._keel.action_access_level", "op": "exists", "value": true}}, "action": "require_human_review"}, {"if": {"not": {"field": "context._keel.action_risk_tags", "op": "exists", "value": true}}, "action": "require_human_review"}, { "if": { "any": [ {"field": "context._keel.action_risk_tags", "op": "contains", "value": "code_exec"}, {"field": "context._keel.action_risk_tags", "op": "contains", "value": "data_export"}, {"field": "context._keel.action_risk_tags", "op": "contains", "value": "egress_external"}, {"field": "context._keel.action_risk_tags", "op": "contains", "value": "pii_access"}, {"field": "context._keel.action_risk_tags", "op": "contains", "value": "refund"}, {"field": "context._keel.action_risk_tags", "op": "contains", "value": "spend"}, {"field": "context._keel.action_risk_tags", "op": "contains", "value": "value_movement"} ] }, "action": "require_human_review" }, {"if": {"field": "context._keel.action_access_level", "op": "eq", "value": "unknown"}, "action": "require_human_review"}, {"if": {"field": "context._keel.action_access_level", "op": "in", "value": ["write", "admin"]}, "action": "deny"}, {"if": {"all": [{"field": "context._keel.action_access_level", "op": "in", "value": ["none", "read"]}, {"field": "context._keel.action_risk_tags", "op": "eq", "value": []}]}, "action": "allow"} ] }

This grant requires both a classified harmless read and an explicitly empty risk-tag list. Missing, null, malformed, or non-empty tags do not satisfy it. Risk-tagged and unknown requests have no grant in this example; approval cannot release them. The platform safety floor also refuses unknown actions. If a project needs a reviewable consequential action, give that action its own bounded grant with all required trusted facts. Use deny instead of REVIEW when the restriction should record outright refusal.

If read/write fields are not enabled for a project, Keel rejects active policies that reference them. An allow rule whose condition can match unknown is a policy validation error (unclassified_action_cannot_allow); rules that match unknown must review, deny, or throttle.

Coverage boundaries

Keel’s strongest read/write coverage is on surfaces where Keel is on the execution path or verifies the downstream credential scope.

SurfaceBoundary
GitHubThe curated registry maps audited GitHub MCP tool names exactly; future or absent names remain unknown. This is classification coverage, not live GitHub journey certification. The github connector identity is enrolled by Keel, not by the customer.
DatabaseCredential-scoped, not SQL-parsed. postgres.readonly/database.readonly classify as read; postgres.readwrite/database.readwrite classify as write; unscoped database identities are unknown.
Stripe and paymentsRetrieve/list tools classify as read; create/update/payment/refund/transfer tools classify as write and usually carry spend; mixed export/report tools are not treated as read.
Files and storageFilesystem, Google Drive, and S3 use exact-name maps. Generic file-like names under the wrong connector are intentionally unmapped.
Slack, Gmail, emailSlack and Gmail have exact-name read/write/admin coverage. Generic email has no read-classified tools until a connector-specific contract pins exact non-mutating semantics.
Common MCPGeneric names alone do not prove semantics. read, list, get, and search are unknown, not read.
Browser or computer-useCoarse. computer.use is ambiguous from the operation alone and rounds up; off-path browser activity is not governed.

The Stripe row describes action classification, not blanket execution coverage. Keel does not expose a first-class general Stripe refund rail. The exact stripe.refund.create action is Keel-enforced only when it uses an enrolled managed MCP :call contract. That beta/limited path has been demonstrated with genuine Stripe TEST refunds; the customer adapter owns the final Stripe HTTP call and TEST credential, live mode is not established, and direct Stripe calls with separate credentials bypass Keel.

Registry coverage is versioned and audited. The policy-authoring catalog reports project MCP coverage from the project’s configured tool allowlists, including the number of unknown tools. Missing entries are safer than wrong read entries: an unmapped tool is refused, while a mutating tool misclassified as read could fail open.

Last updated on Edit this page on GitHub