Reference · Security

Everything the engine enforces, and everything it leaves to you.

Ion runs in operator-controlled infrastructure. It requires no Ion-operated runtime service. The operator selects the deployment topology, model providers, extensions, identity integrations, and telemetry destinations.

Permissions

allow

Every tool call passes unless a rule says otherwise.

Dangerous patterns

Enabled

35+ compiled patterns, always evaluated.

Bash sandbox

Disabled

Seatbelt or bwrap, opt-in, Bash only.

Secret redaction

Disabled

Opt-in scanner on tool output.

Three of the four controls above ship off. Ion ships primitives, not a security policy. Enterprise configuration can set stronger minimums and seal them so no lower layer weakens them.

01 / Decisions

Set the policy. Watch what happens to a tool call.

Rules are evaluated in order and the first match decides. Change the mode and the toggles: every verdict below is recomputed from the same rules the engine applies.

Permission mode

Opt-in controls

Rules in effect

0Read → allow
1Bash → allow, commandPatterns ["^git\\s"]
2Write → deny, pathPatterns ["/etc/*"]
—readOnlyPaths ["/etc/**", "node_modules/**"]

Evaluated in order. The first rule that matches decides. If none match, the mode above decides.

Six tool calls under this policy2 allow · 3 ask · 1 deny

The audit entry that call writes

{
  "timestamp": 1714000000000,
  "tool": "Bash",
  "input": { "command": "rm -rf /tmp/build" },
  "decision": "ask",
  "reason": "dangerous pattern, operator prompted",
  "rule": "",
  "sessionID": "sess_abc123"
}

Entries record the decision and its reason, never the tool output. They flow through telemetry to a file, an HTTP endpoint or OTLP.

Rules and dangerous patterns are separate systems. Rules are checked first; an explicit allow runs the command without dangerous-pattern review. Read the source: permissions.md, dangerous-patterns.md, audit.md.

02 / Boundary

Where your infrastructure ends.

Three kinds of traffic leave the host. Turn one on to see where it goes and what makes it go. The map names possible flows instead of claiming a single egress path.

Operator boundary · your host

~/.ion created 0700
Clientyours
The engineours
Built-in toolsin-process
Extensions and subprocessesyours
Local systems and logsyour host

Tools run where the engine is deployed, not where the person is. In a split-host deployment, client traffic crosses from the client host to the engine host over the listener you configured.

Configured external services

Required when selected

Model provider traffic. Prompts and responses.

Operator-configured

Extension and tool API traffic. Request data and results.

Operator-configured

Identity provider traffic. Authentication and token exchange.

Optional or policy-controlled

Audit, operational-log and telemetry export.

Provider traffic, tool and identity traffic, and log export. Host network policy remains the operator control for all three.

EGRESS CONTROL
FIELD
BEHAVIOR
Provider traffic
providers.*
Required when a provider is selected. Prompts and responses go to the endpoint you configured.
Corporate proxy
network.proxy.httpsProxy
Routes outbound HTTP and HTTPS. noProxy accepts hosts, domain suffixes, IPs and CIDR ranges.
Custom CA bundle
network.customCaCerts
PEM files added to the trust store alongside the system bundle, for TLS-inspecting proxies and internal CAs.
TLS verification
network.rejectUnauthorized
True by default. False disables verification for every outbound connection, including provider calls.
Log and telemetry export
logging.egressTargets
Empty by default, meaning local file only. http POSTs batches; otel exports OTLP records.

Enterprise network configuration replaces lower-layer network settings entirely, and a sealed rejectUnauthorized: true cannot be turned off below it. Source: enterprise/network.md.

03 / Controls

Control review matrix

Eight controls, their defaults, and whether enterprise policy can seal them. Open a row for the enforcement point, the documented limit and the source file.

CONTROL
DEFAULT
SURFACE
SEALABLE
Permission engineallowTool callsYes+Dangerous patternsEnabledBash commandsYes+Bash sandboxDisabledBash subprocessesYes+Secret redactionDisabledTool output to modelYes+Audit loggingWith permissionsPermission decisionsYes+Credential brokeringIdentity dependentExtension HTTP callsHost policy+Network and TLS policySystem defaultsAll outbound trafficYes+Enterprise sealed policyNot setSession configYes+

Showing 8 of 8 controls. Sealable means enterprise configuration can set the value and prevent a lower layer from weakening it.

04 / Enforcement

One tool call, eight stages.

Every tool call passes through all eight, in this order. Pick one to see what it does and what it cannot do.

Stage 02

Evaluate ordered permission rules

The engine walks the rules array from index 0. For each rule it checks the tool name, then the optional command or path patterns. The first rule that matches decides. If none match, the mode default decides.

What it does not do

Rule order is the policy. A broad allow placed before a narrow deny silently wins, and nothing warns about the shadowing.

docs/security/permissions.md

05 / Configuration

The files that carry the policy.

The permission policy, the audit entry it produces, the enterprise config that seals it, and the network settings that govern outbound traffic.

A policy with a mode, ordered rules, extra dangerous patterns and read-only paths. Rules are checked in array order.

{
  "permissions": {
    "mode": "ask",
    "rules": [
      { "tool": "Read", "decision": "allow" },
      { "tool": "Bash", "decision": "allow", "commandPatterns": ["^git\\s"] },
      { "tool": "Bash", "decision": "deny",  "commandPatterns": ["rm\\s+-rf"] },
      { "tool": "Write", "decision": "deny", "pathPatterns": ["/etc/*", "/usr/*"] }
    ],
    "dangerousPatterns": ["(?i)drop\\s+database", "terraform\\s+destroy"],
    "readOnlyPaths": ["/etc/**", "/usr/**", "node_modules/**"]
  },
  "security": { "redactSecrets": true },
  "sandbox": { "enabled": true }
}

How enterprise config seals a value

Enterprise configuration is not another layer in the merge. It is a constraint applied after defaults, user and project have merged, and it only ever removes. That ordering is what makes the permissive merge below it safe.

SEALING
FIELDS
WHAT LOWER LAYERS CAN DO
Restrictive
allowedModels, blockedModels, allowedProviders, permissions.mode, toolRestrictions.deny, sandbox.required, sandbox.allowDisable
Only tighten. Enterprise ask means user and project cannot set allow; deny is still honored because it is stricter.
Additive
permissions.rules, permissions.dangerousPatterns, permissions.readOnlyPaths, sandbox.additionalDenyPaths, mcpDenylist
Union merge. Enterprise rules are prepended, so they evaluate before anything a project or user wrote.
Override
network, telemetry, requiredHooks, newConversationDefaults
Enterprise replaces lower layers outright. Telemetry enabled at this layer cannot be disabled below it.
Filtering
mcpAllowlist, toolRestrictions.allow, planModeAllowedBashCommands
Applied after the merge, removing anything not sanctioned. User and project merge additively below the ceiling.

The final config is immutable for the session lifetime. Source: enterprise/sealed-config.md.

06 / Credentials

Extension code never holds a token.

For a service behind an identity provider, the engine brokers the token. It holds the identity, mints a short-lived token scoped to exactly what the code asked for, and sets the authorization header itself. An authorization header an extension tries to set is overwritten. Calls to private and reserved addresses are rejected by default; an extension that needs an intranet API opts out per request, and that opt-out is an operator decision, visible in source.

An authenticated call as the signed-in operator. Notice what is absent.
const res = await ctx.http.get('https://graph.microsoft.com/v1.0/me', {
  scope: 'https://graph.microsoft.com/User.Read',
})

What brokering does not claim

This is not a claim that a token can never leave. The extension makes the call the token rides on, so end-to-end containment still depends on your egress controls. What the engine removes is the token handling, and the accidental exposure that comes with it.

07 / Limits

Who is responsible for what.

Ion ships

The engine binary, the opt-in controls, and the enforcement path documented above. Nothing else. There is no Ion-operated runtime service.

You select

The deployment topology, which executable extensions run, identity and telemetry destinations, and host network policy.

Providers process

Whatever traffic you send them, under their own terms. Ion neither brokers nor inspects that relationship.

The platform provides

The sandbox support available on that OS. macOS has Seatbelt, Linux needs bwrap installed, Windows has nothing.

Documented limits, in one list

  • The controls are opt-in. Three of the four ship off.
  • The sandbox covers Bash only, and only on macOS and Linux. Read, Write and Edit are governed by permissions, not the sandbox.
  • Redaction runs after execution and is pattern-based. Novel credential formats can pass; test fixtures can be over-redacted.
  • Audit entries record tool input and decision facts. They do not record tool output or redacted values.
  • The operator selects which executable extensions run. Extension authors control their code and the APIs it calls.
  • Model providers process the traffic sent to them. Host network policy remains the outbound control.
  • Only the latest release of each component receives security fixes. There is no backport.

Report vulnerabilities through GitHub Security Advisories. Policy: SECURITY.md.

08 / Evidence

Evidence for review.

Next: how it actually runs in your environment

Deployment