EasyCC
For admins

Configuration

Managed policy, identity, and the three-layer config model.

EasyCC reads three layers of configuration on every launch. Once you understand how they compose, the rest of admin config is just paste-the-right-snippet.

For the full list of every supported policy field, see Reference → Policy fields.

The three-layer model

LayerOwnerWhere it livesMutability
AppConfigUser<config_dir>/EasyCC/config.jsonUser can change from Settings
ManagedPolicyIT adminRegistry / plist (managed)Pushed via MDM; user can't change
BundleService providerbootstrap.jsonAuthored by partner; applied on first launch

Three sources, three owners, one composition point per read. Writes follow ownership — admin values never round-trip through user state.

Composition rule for every read:

  1. Managed policy with kind: enforced wins.
  2. Otherwise, bundle with kind: enforced.
  3. Otherwise, managed policy with kind: default.
  4. Otherwise, bundle with kind: default.
  5. Otherwise, the user's AppConfig value.
  6. Otherwise, the built-in default.

In short: enforced beats default; admin beats partner; partner beats user; user beats built-in.

Managed policy is re-read on every launch. It's never written to disk by EasyCC, so when an MDM unenrolls the device the next launch reads "no policy" and the user's own settings come back instantly. This also means a user can't tamper with admin-pushed values by editing a config file — there isn't one to edit.

Where managed policy lives

Windows

ContainerPathBehavior
EnforcedHKEY_LOCAL_MACHINE\Software\Policies\EasyCCTop-level values are enforced — user can't override
RecommendedHKEY_LOCAL_MACHINE\Software\Policies\EasyCC\RecommendedAdmin-recommended defaults — user can change

The two-container split matches Chrome Enterprise's convention. If you've shipped Chrome via ADMX, you already know this shape.

macOS

ContainerWhere in the plistBehavior
EnforcedTop-level keys in /Library/Managed Preferences/com.easycc.app.plistEnforced
RecommendedKeys nested under a recommended dict in the same plistDefaults the user can change

Linux

Not supported in v1. The discovery layer returns empty silently so the app boots cleanly; v2 may add /etc/easycc/policy.json.

Every field is one of two kinds:

KindWhat it doesWhat the user sees
EnforcedUser can't overrideDisabled control with a lock icon and the tooltip "Managed by your organization — you can't change this."
RecommendedAdmin sets a defaultNormal control. The user has no UI cue the value came from policy (matches Chrome / Slack convention).

Pick enforced when compliance, security, cost, or correctness require the value to be non-negotiable for everyone in the org. Pick recommended when you have a sensible starting point but personal preference is fine.

If both are set for the same field, enforced wins (EasyCC logs a warning). If neither is set, the user's setting wins.

Copy-paste snippets

Windows (PowerShell)

# Enforce stable channel + lock dev mode off + 7-day update deadline
New-Item -Path 'HKLM:\Software\Policies\EasyCC' -Force | Out-Null
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC' -Name 'release_channel'          -Value 'stable' -Type String
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC' -Name 'dev_mode'                 -Value 0        -Type DWord
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC' -Name 'force_update_after_hours' -Value 168      -Type DWord

# Recommend 72-hour deferral (user can change)
New-Item -Path 'HKLM:\Software\Policies\EasyCC\Recommended' -Force | Out-Null
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC\Recommended' -Name 'max_update_deferral_hours' -Value 72 -Type DWord

# Safety scanner: regulated org
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC' -Name 'safety_deep_check_enabled' -Value 1            -Type DWord
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC' -Name 'safety_sensitivity'        -Value 'sensitive'  -Type String

# Pin a self-hosted update mirror
Set-ItemProperty 'HKLM:\Software\Policies\EasyCC' -Name 'update_endpoint' -Value 'https://updates.your-org.example/easycc/latest.json' -Type String

# Remove all managed policy
Remove-Item -Path 'HKLM:\Software\Policies\EasyCC' -Recurse -Force -ErrorAction SilentlyContinue
EasyCC field typePowerShell -Type
string / enum / URL / pathString
booleanDWord (1 = true, 0 = false)
integerDWord

macOS (plist)

Save as /Library/Managed Preferences/com.easycc.app.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <!-- Enforced -->
  <key>release_channel</key>            <string>stable</string>
  <key>dev_mode</key>                   <false/>
  <key>force_update_after_hours</key>   <integer>168</integer>
  <key>safety_deep_check_enabled</key>  <true/>
  <key>safety_sensitivity</key>         <string>sensitive</string>

  <!-- Recommended -->
  <key>recommended</key>
  <dict>
    <key>max_update_deferral_hours</key> <integer>72</integer>
  </dict>
</dict>
</plist>

Deploy via Jamf, Kandji, Workspace ONE, Mosyle, or any tool that writes to /Library/Managed Preferences/.

Service-provider bundle

{
  "policy": {
    "release_channel":           { "kind": "enforced", "value": "stable" },
    "max_update_deferral_hours": { "kind": "default",  "value": 72 }
  }
}

Verifying a policy

After pushing, on a target device:

Windows:

Get-ItemProperty 'HKLM:\Software\Policies\EasyCC' | Format-List
Get-ItemProperty 'HKLM:\Software\Policies\EasyCC\Recommended' | Format-List

macOS:

sudo defaults read /Library/Managed\ Preferences/com.easycc.app

In EasyCC: Settings → Advanced should show lock icons next to the enforced fields. If the keys are there but EasyCC doesn't reflect them, restart the app (managed policy is read at launch).

For "I pushed it and EasyCC doesn't see it," see Support.

Sign-in and identity

EasyCC v1 supports two ways to sign in: email + one-time-password code, and Microsoft work or school account. Apple, Google, and GitHub are planned (mid-2026). This is the user's sign-in to EasyCC itself (managed by Supabase) — distinct from the Claude provider config (which credentials Claude uses for chats).

Sign-in tokens (auth_session_token, auth_refresh_token) live in the OS keychain under service com.easycc.app. EasyCC manages them — don't push or rotate.

Microsoft Entra setup

To enable "Sign in with Microsoft" for users in your Microsoft 365 tenant:

EasyCC requests these Microsoft Graph scopes only — no mailbox, no calendar, no data read/write:

ScopeWhy
openidOIDC sign-in
emailRead the user's email to identify them
profileRead display name
offline_accessKeep the session active across launches via refresh token

One-time admin consent. The first user in your tenant may see "Need admin approval". To approve in advance:

  1. Sign in to entra.microsoft.com as Global Administrator or Application Administrator.
  2. Identity → Applications → Enterprise applications → search "EasyCC" (or add it).
  3. Permissions tab → Grant admin consent for your tenant.

After consent, all users in your tenant can sign in without prompts.

Conditional access policies apply. MFA requirements, compliant-device checks, trusted-location restrictions, and risk-based blocking all kick in at sign-in time. EasyCC handles the resulting browser flow correctly.

Tenant restrictions

EasyCC doesn't currently expose a tenant allowlist at the EasyCC layer — restrict via Entra conditional access (block users outside specific groups), and combine with deploying EasyCC only to managed devices. A hard "Microsoft-only" lock at the EasyCC layer is not in v1 — talk to us if it's a blocker.

What's not in v1

  • Apple, Google, GitHub sign-in (planned mid-2026)
  • SAML or OIDC SSO beyond Entra
  • SCIM directory sync (v2)
  • Custom OAuth providers (not on the roadmap — open a request if you need one)

Auditing sign-in

EasyCC writes structured auth_signin / auth_signout entries to:

PlatformLog path
Windows%LOCALAPPDATA%\com.easycc.app\logs\
macOS~/Library/Logs/com.easycc.app/

Forward these to your SIEM via your log shipper. To force sign-out across the fleet, revoke at the provider (Supabase or Entra) — the next refresh attempt fails and the user is signed out.

Where to look next

How is this guide?

On this page