AI Native Lang
security

Capability Grants: A Safer Security Model for AI Runtimes

How AINL’s restrictive-only capability grant model and named security profiles let operators lock down AI runtimes without paralyzing teams.

March 17, 2026·7 min read
#security#capability-grants#profiles#sandbox
Share:TwitterLinkedIn

TL;DR

Most AI runtimes still behave like “god processes”: if an agent can reach the runtime, it can often do almost anything the host can.

AINL’s capability grant model takes a different approach:

  • every execution surface (runner, MCP server) loads a server-level grant from a named security profile;
  • callers attach their own policy; the host merges them using restrictive-only rules — you can tighten permissions, but never widen them;
  • adapters carry rich metadata (privilege tiers, destructive, network_facing, sandbox_safe) so policies can be expressed in terms of capabilities, not just raw names.

This post explains how capability grants work in AINL and why they’re a safer default for AI runtimes.


The “god key” problem in AI runtimes

As more teams move from LLM demos to production agents, a recurring problem appears:

The runtime has broad, ambient access to everything. If an agent can hit the right endpoint with the right token, it can do far more than it should.

Common symptoms:

  • runners with full outbound network + filesystem + DB access;
  • shared credentials that don’t encode which tools or effects were intended;
  • policy that lives mostly in prompts (“please don’t delete anything”) instead of enforceable constraints;
  • difficulty answering “which capabilities did this agent actually have?”.

AINL’s capability grant model is designed to tackle this directly.


Capability grants: the core idea

At a high level, a capability grant says:

“For this host and this request, these are the adapters, effects, and privilege tiers that are allowed. Everything else is forbidden.”

In AINL, each execution surface:

  • runner service (HTTP /run, /enqueue, /capabilities, etc.);
  • MCP server (ainl-mcp for IDEs and agent hosts);

loads a server-level grant at startup from a named security profile, then merges it with per-request restrictions.

Key properties:

  • restrictive-only merge: callers can’t expand what the server allows — they can only add more restrictions;
  • capabilities instead of identities: what matters is which adapters/effects are allowed, not which user/agent you are;
  • machine-readable: capabilities and profiles are JSON, not prose in a runbook.

Named security profiles at startup

Security profiles live in tooling/security_profiles.json. Each profile bundles:

  • an adapter allowlist;
  • forbidden adapters/effects/privilege tiers;
  • runtime limits (max steps, depth, adapter calls, time);
  • orchestrator expectations (network rules, container constraints).

Examples (simplified):

  • local_minimal

    • adapters: core only;
    • forbidden privilege tiers: local_state, network, operator_sensitive;
    • limits: very small; intended for local dev and dry-run usage.
  • sandbox_network_restricted

    • adapters: core + filesystem + SQLite + HTTP + queue, etc.;
    • no “operator_sensitive” adapters (email, calendar, social, db);
    • expects strict egress allowlists at the network layer.
  • operator_full

    • adapters: operator‑defined full surface;
    • intended for tightly controlled, operator-managed environments with their own policy engines and network controls.

At startup, the runner loads a profile via:

  • AINL_SECURITY_PROFILE=<profile> for the HTTP runner;
  • AINL_MCP_PROFILE=<profile> for the MCP server.

That profile becomes the server grant — the outermost capability envelope.


Restrictive-only merge: callers can’t widen the grant

When a caller submits a request (for example, to /run or via MCP tools), they can attach an optional policy object that further constrains the run:

  • forbidden adapters,
  • forbidden effects / effect tiers,
  • forbidden privilege tiers,
  • stricter runtime limits.

The host applies a restrictive-only merge between:

  1. the server-level grant (from the named security profile), and
  2. the caller’s policy.

Rules of the merge:

  • anything forbidden in either grant is treated as forbidden;
  • limits (steps, depth, time, calls) take the minimum of the two;
  • callers cannot enable adapters or privilege tiers that the server grant does not allow at all.

This guarantees:

  • no client, IDE, or agent can “turn on” destructive capabilities that the server didn’t already permit;
  • operators can centralize the broad shape of access, while callers refine it.

Capability-aware adapter metadata

To make capability grants expressive, AINL adapters carry rich metadata in tooling/adapter_manifest.json, including:

  • verbs and effect defaults;
  • privilege tiers (e.g. core, local_state, network, operator_sensitive);
  • flags like:
    • destructive,
    • network_facing,
    • sandbox_safe.

This metadata shows up in:

  • the HTTP /capabilities endpoint; and
  • the MCP resource ainl://adapter-manifest.

With this, policies can say things like:

  • “forbid all destructive adapters”;
  • “forbid all operator_sensitive privilege tiers”;
  • “only allow adapters marked sandbox_safe in this environment”.

…without hard-coding adapter names everywhere.


How this looks in practice

Imagine three environments:

  1. Local dev: AINL_SECURITY_PROFILE=local_minimal

    • only core ops allowed;
    • no I/O, no network, no durable state;
    • perfect for testing compiler/runtime behavior without side effects.
  2. Sandboxed staging: AINL_SECURITY_PROFILE=sandbox_network_restricted

    • filesystem, SQLite, cache, HTTP, tools, queue;
    • no email/calendar/db/social adapters;
    • network egress locked to a small set of internal hosts.
  3. Trusted operator production: AINL_SECURITY_PROFILE=operator_full

    • operator decides which adapters to enable per deployment;
    • network and secrets locked down by infra;
    • strong observability and audit pipelines in place.

Now add a caller policy, for example:

{
  \"forbidden_privilege_tiers\": [\"operator_sensitive\"],
  \"forbidden_destructive\": true,
  \"max_steps\": 1000,
  \"max_time_ms\": 15000
}

Regardless of what the caller sends:

  • if the server grant doesn’t allow an adapter, the policy can’t resurrect it;
  • forbidden_destructive: true will reject any adapter marked destructive;
  • runtime limits will be no higher than what the server profile permits.

The runtime responds with a structured 403 and a list of violations if the compiled graph tries to violate the effective policy — without executing the workflow.


Why this is better than “just prompts”

Prompt-level guardrails (“please don’t call dangerous tools”) are fragile:

  • they rely on the model behaving as instructed;
  • they aren’t machine-checkable;
  • they’re hard to audit over time.

Capability grants give you:

  • a formal, checkable contract about what a runtime instance can and cannot do;
  • clear separation between:
    • what capabilities exist (adapter manifest),
    • which are enabled in a given environment (security profile),
    • how they are further restricted per request (policy);
  • enforcement in code, not in vibes.

For security and platform teams, this is a much more familiar, auditable model.


Using capability grants with MCP

The same model applies to the MCP server (ainl-mcp):

  • AINL_MCP_PROFILE chooses a base security profile for MCP tools;
  • AINL_MCP_EXPOSURE_PROFILE and env vars like AINL_MCP_TOOLS / AINL_MCP_RESOURCES limit which tools/resources are even visible;
  • hosts (e.g. Gemini CLI, Claude Code) can treat AINL as a least-privilege workflow tool, not a backdoor into your infrastructure.

This lets you do things like:

  • expose only ainl_validate and ainl_compile to IDEs (no ainl_run);
  • expose only ainl://adapter-manifest and ainl://security-profiles to security dashboards;
  • run ainl-mcp behind a gateway with its own policies on top.

When to reach for capability grants

You should care about capability grants if:

  • you run AI workflows in shared or multi-tenant environments;
  • you have strict requirements around:
    • outbound network control,
    • secrets exposure,
    • auditability and change review;
  • you want a model that security engineers and SREs can reason about.

AINL’s capability grant model is not a silver bullet, but it is:

  • safer by default than “agent + god token”;
  • explicit about where security decisions live;
  • consistent across runner and MCP surfaces.

If you already run AINL, the next step is to:

  1. Pick an appropriate security profile in tooling/security_profiles.json.
  2. Set AINL_SECURITY_PROFILE (runner) and/or AINL_MCP_PROFILE (MCP) in your environments.
  3. Start tightening policies per environment, rather than relying solely on prompts and human convention.

It’s a small change in configuration, but a big one in how seriously you can take AI runtimes as part of your production infrastructure.

A

AI Native Lang Team

The team behind AI Native Lang — building deterministic AI workflow infrastructure.

Related Articles