Skip to content
VediramVediramSupport

How Policy Works

What Levee policy is, where it lives, how it reaches running projects, and how local and cloud policy combine

Policy decides what the AI agent in a Levee project may reach: which websites, which AI models, which databases, which files and which tools. Your IT team writes it, and Levee enforces it outside the agent's sandbox. The agent can neither read nor change it.

By default nothing is allowed. A new machine with no policy is safe but not yet useful: the agent cannot reach the web, call a model, or query a database until policy allows it.

This page explains the model. The pages that follow cover each channel in detail.

Where does policy live?

Each computer has one policy file:

%ProgramData%\Vediram\Levee\policy.json

It gets there in one of two ways:

SourceHow it arrivesTypical use
Vediram Portal (cloud)The Levee app downloads your organization's policy as the signed-in user, and the Levee service writes it to policy.json. See Managing Policy in the Vediram Portal.Computers connected to Vediram Cloud
Local fileAn administrator writes policy.json directly, or deploys it with Intune, Configuration Manager, Group Policy preferences, or any file-distribution tool.Offline machines, pilots, machines not connected to Vediram Cloud

The installer does not create a policy.json. Until one exists, Levee uses its built-in defaults, which refuse every web, model, database, search and Office request.

Only administrators should be able to write to %ProgramData%\Vediram\Levee\policy.json. Standard users need no access to it; the Levee app reads the policy in force through the Levee service.

What does policy control?

policy.json is a single JSON object. Each top-level key is a section. The proxies section holds one sub-section per channel, that is, one kind of thing the agent can reach.

SectionControlsReference
proxies.httpWebsites and online services the agent may reachWeb Access
proxies.llmWhich AI models the agent may use, through which provider, with which budgetsModel Access
proxies.odbcDatabase queriesDatabase Access
proxies.searchWeb search (Preview)Web Search
proxies.m365Word, Excel, PowerPoint and Outlook operations (Preview)Microsoft 365
proxies.pkgPython and JavaScript package installs (Preview)Packages
proxies.fsFiles and folders outside the projectFile Access
proxies.mcpPlugins that give the agent extra tools (Preview)Plugins
containerMemory, environment variables, sandbox image, default agentContainer Settings
runtimeProvisioningWhere and how the sandbox image is downloadedContainer Runtime
webIdentityWhether users may sign the agent in to websitesConnected Identities
telemetryWhether audit events are sent to Vediram CloudActivity & Audit

Which rules apply to every policy?

These rules hold for every section.

Default deny. A channel with no section in the policy is closed. A missing proxies.http section behaves exactly like { "default": "deny", "rules": [] }. Nothing is reachable unless a rule allows it.

Fail closed. If a channel cannot enforce policy (for example, its component did not start), the agent does not get open access instead. The request is refused, or the project refuses to start.

Last good policy wins. If policy.json is missing, empty, or contains invalid JSON, the Levee service keeps the policy it already had and logs a warning. A typo in a file you are editing never opens access. If no valid policy was ever loaded, the built-in deny-all defaults apply.

Each section is checked on its own. Each channel validates its own section when it receives it. A section that fails is rejected, and the channel keeps its previous rules (or refuses everything if it never had any). For example, a model-access section that contains an API key is rejected as a whole.

The same refusal every time. When a request is refused, the agent receives the same generic answer whatever the reason, so it cannot work out your policy by trial and error. The reason is recorded in the audit trail. See Agent gets "Access denied.".

Every decision is audited. Allowed, refused, and approved by a person: each decision is recorded in the audit trail. See Activity & Audit.

When do changes take effect?

Most changes apply within seconds, without restarting anything. The Levee service watches policy.json and, when it changes, reloads it and sends the new rules to the running project. This covers:

  • web access rules
  • model access, database, search and Office rules
  • plugins added, updated or removed (once plugins are turned on, see Plugins)

A few settings are fixed when a project's sandbox is created. They apply the next time the project starts:

  • container.memoryLimitMb
  • container.environmentVariables
  • container.defaultAgentTool (for new chat sessions)

container.baseImage decides which sandbox image the machine downloads. See Container Settings.

For your IT team: reload and inspect

To force a reload (for example after restoring a file with an older timestamp):

levee policy reload

To see the policy the service is actually enforcing:

levee policy show
levee policy show --json | Out-File effective-policy.json

How do local and cloud policy combine?

When a computer is connected to Vediram Cloud, the cloud policy is merged into the local file. Web access always comes from the cloud. Any other section the cloud policy contains replaces the local one. Sections the cloud policy leaves out stay as they are locally.

The Levee app checks for policy updates when it starts, when it reconnects to the Levee service, every 5 minutes, and when the user clicks Sync now in Settings. The merge rules in detail:

  • Web access is always taken from the cloud policy. The proxies.http section is rebuilt from the cloud document every time, and refuses everything if the cloud document has no web rules. Local edits to proxies.http on a cloud-managed machine are overwritten at the next sync.
  • Any other section present in the cloud policy replaces the local one. For example, if the cloud policy has a container section, the local container section is replaced.
  • Any section absent from the cloud policy is left as it is locally. You can, for example, manage model access centrally while keeping machine-specific database rules in the local file.
  • Within proxies, replacement is per channel. A cloud policy that carries only proxies.search replaces the local search section and leaves the local proxies.odbc section untouched.

If a sync fails (the computer is offline, the user is signed out, the service rejects the document), the policy already on disk stays in force. A partly downloaded policy is never applied.

Applying a cloud-format policy by hand

For offline machines, or to test a policy before publishing it, you can apply a cloud-format policy document (the AgentPolicy format the Portal produces) from a file. The same merge rules apply:

levee policy sync --file .\agent-policy.json

A minimal cloud-format document looks like this:

{
  "apiVersion": "policy.vediram.com/v1alpha1",
  "kind": "AgentPolicy",
  "metadata": { "name": "finance-default" },
  "spec": {
    "targets": ["levee"],
    "egress": {
      "default": "deny",
      "allow": ["api.anthropic.com", "cdn.anthropic.com", "learn.microsoft.com"]
    },
    "levee": {
      "network": {
        "rules": [
          { "domain": "*.example-data-provider.com", "ports": [443], "action": "allow" }
        ]
      },
      "container": { "memoryLimitMb": 8192 },
      "proxies": {
        "llm": { "default": "deny", "budgets": { "perProjectDailyTokens": 2000000, "perProjectSessionTokens": 500000 }, "providers": { "anthropic": {} }, "models": [ { "name": "claude-sonnet-4-5", "provider": "anthropic", "upstreamModel": "claude-sonnet-4-5" } ] }
      }
    }
  }
}

In a cloud-format document:

  • spec.egress.allow is a list of bare host names. Each becomes an allow rule on port 443.
  • spec.egress.default sets the default (deny unless set to allow).
  • spec.levee.network.default (allow or deny), if present, overrides spec.egress.default.
  • spec.levee.network.rules uses the full web access rule syntax. A rule for a host that also appears in egress.allow replaces the simple allow rule for that host.
  • spec.levee.container, spec.levee.proxies, spec.levee.runtimeProvisioning, spec.levee.webIdentity and spec.levee.telemetry use exactly the same keys as the corresponding policy.json sections.
  • spec.plugins lists the plugins your organization enabled. Levee keeps it separately from policy.json. See Plugins.

A complete example

The following local policy.json is for a finance team. It lets the agent read a few public reference sites, use Claude models through Anthropic with daily budgets, run read-only queries against one reporting database, and search the web with the user's approval for each query. JSON does not allow comments; the explanations follow the example.

{
  "container": {
    "memoryLimitMb": 8192,
    "defaultAgentTool": "claude-code",
    "environmentVariables": {
      "PYTHONUTF8": "1"
    }
  },
  "proxies": {
    "http": {
      "default": "deny",
      "rules": [
        { "domain": "api.anthropic.com", "action": "allow" },
        { "domain": "cdn.anthropic.com", "action": "allow" },
        { "domain": "learn.microsoft.com", "action": "allow" },
        { "domain": "www.ecb.europa.eu", "action": "allow" },
        { "domain": "*.wikipedia.org", "action": "escalate" }
      ]
    },
    "llm": {
      "default": "deny",
      "budgets": {
        "perProjectDailyTokens": 2000000,
        "perProjectSessionTokens": 500000,
        "perProjectMaxConcurrentRequests": 4
      },
      "providers": { "anthropic": {} },
      "models": [
        { "name": "claude-sonnet-4-5", "provider": "anthropic", "upstreamModel": "claude-sonnet-4-5" },
        { "name": "claude-opus-4-1", "provider": "anthropic", "upstreamModel": "claude-opus-4-1", "promptOnFirstUse": true }
      ]
    },
    "odbc": {
      "default": "deny",
      "rules": [
        {
          "alias": "reporting",
          "hostDsn": "DSN=Reporting-ReadOnly",
          "allowedVerbs": ["SELECT"],
          "maxRowsPerQuery": 1000,
          "queryTimeoutSeconds": 30,
          "tables": { "allow": ["sales.*"], "deny": ["sales.salaries"] }
        }
      ]
    },
    "search": {
      "enabled": true,
      "defaultProvider": "tavily",
      "allowedProviders": ["tavily"],
      "maxQueriesPerSession": 50,
      "approval": { "mode": "per-query" },
      "providerKeys": { "tavily": "LEVEE_SEARCH_KEY_TAVILY" }
    }
  },
  "webIdentity": { "enabled": false },
  "telemetry": { "forwardingEnabled": true }
}

What this policy does:

  • container: each sandbox may use up to 8 GB of memory, the chat starts Claude Code by default, and one extra environment variable (Python's UTF-8 mode) is set inside every sandbox.
  • proxies.http: everything is refused except the listed sites. Claude Code can reach Anthropic. Two reference sites are allowed. Requests to Wikipedia pause and ask the user. See Web Access.
  • proxies.llm: the agent may use two Claude models. The more expensive one asks the user the first time a project uses it. Each project is capped at 2 million tokens per day and 500,000 tokens per run. The Anthropic key is stored on the computer, never in the file. See Model Access.
  • proxies.odbc: the agent sees one data source named reporting, can only run SELECT, never sees the salaries table, and receives at most 1,000 rows per query. See Database Access.
  • proxies.search: the agent gets a web search tool backed by Tavily, and the user approves each query before it leaves the computer. See Web Search.
  • webIdentity: users cannot sign the agent in to websites with their own browser session.
  • telemetry: audit events are sent to Vediram Cloud so they appear in the Portal (the computer must be connected to Vediram Cloud).

Checking your work

  1. Check that the file is valid JSON before deploying it:

    Get-Content "$env:ProgramData\Vediram\Levee\policy.json" -Raw | ConvertFrom-Json | Out-Null
    
  2. Confirm the service loaded it:

    levee policy show
    
  3. Start a project and watch the decisions arrive in the app's Activity tab, or from the command line:

    levee audit tail --follow
    

levee config validate checks config.json (the logging configuration), not policy.json. See Logging.