Policies & inheritance

Three levels of policy (tenant / group / device), 10 sections, 150+ settings. How the merge works, and how the agent applies it.

Three-level inheritance

A device's effective policy is computed by merging three layers in order:

  Tenant default policy       ← base for everything
       +
  Group policy override       ← overrides tenant where set
       +
  Device policy override      ← overrides group where set
       =
  Effective policy            ← what the agent actually applies

Merging is section-aware: each of the 10 sections (security, apps, display, ...) merges independently. If a group sets security.camera_disabled = true, that value wins for that section — but the tenant's apps.hidden_packages list still applies.

Arrays do notmerge — the most specific level's array replaces higher levels entirely. This prevents accidental blacklist union.

In the console, the policy detail page has a Preview effective policy button that shows you exactly what a device will receive — no math required.

The 10 policy sections

  • security — password quality, camera, screen capture, keyguard, FRP, MTE, content protection
  • apps — hidden packages, blocked uninstall, suspended packages, default permission policy
  • display — orientation lock, brightness mode, screen timeout
  • lockdown — status bar, gesture navigation
  • restrictions — 54 UserManager DISALLOW_* toggles (bluetooth, WiFi, NFC, SMS, etc.)
  • network — VPN, proxy, WiFi SSID allowlist, private DNS, NFC, 802.1X
  • branding — wallpaper URL, device name template, support messages
  • usb — storage block/allow, data signaling
  • kiosk — lock task packages, web kiosk URL, status bar, settings access
  • system — auto time/timezone, update windows, backup
  • agent — heartbeat interval, screenshot quality, compliance interval, log level

Full schema: Policy JSON schema reference.

How an edit propagates

  1. Admin clicks Save in the policy editor
  2. API validates the JSON against the policy schema (rejects on invalid)
  3. API writes a new row to policies with an incremented version
  4. API computes effective policy for every affected device (tenant, group, or device-scoped)
  5. API publishes policy_update MQTT command with the merged JSON to each device
  6. Agent receives, calls PolicyEngine.applyPolicy(), runs 75+ DPM calls
  7. Agent publishes compliance report to vela/{id}/policy_report
  8. Console shows updated compliance status within ~5 seconds

Policy diff viewer

Every save creates a new version. The Historytab on any policy shows a side-by-side diff of what changed between versions — not a raw JSON diff, but a human-readable summary like "Camera disabled: false → true".

Click Revert to version N to roll back. This creates version N+1 with the old values (policy history is append-only).

Policy templates

The console ships with built-in templates you can use as starting points:

  • Kiosk (single app) — lock task mode, hide status bar, disable settings
  • Kiosk (web) — fullscreen WebView locked to a URL
  • Digital signage — screen always on, auto-rotate, wallpaper + playlist hooks
  • VDI thin client — Citrix/Horizon/AVD allowlisted, restricted user settings
  • Shared desktop — minimal restrictions for browser + office use

Example: tenant-wide security baseline

tenant-default policy (partial)json
{
  "version": 3,
  "security": {
    "password_complexity": "medium",
    "password_min_length": 6,
    "screen_capture_disabled": true,
    "factory_reset_protection": true,
    "max_failed_attempts": 10
  },
  "restrictions": {
    "install_unknown_sources": true,
    "config_credentials": true
  },
  "agent": {
    "heartbeat_interval_seconds": 60,
    "log_level": "info"
  }
}

Every device in the tenant gets these values unless a group or device override supplies a different value. Creating a "Kiosk" group that sets kiosk.web_url = "https://signage.app" does not disturb the security settings.

AI-ready schema

The API exposes GET /api/v1/policies/schema which returns the full JSON Schema describing every policy field, type, and allowed values. This is what drives the console policy editor — and it also means an LLM can generate a valid policy from natural language:

curl https://api.velaos.ch/api/v1/policies/schema \
  -H "X-API-Key: vela_live_sk_..."

Next steps

Was this helpful?
Updated 2026-04-14Edit on GitHub