A managed policy lets an administrator configure T3 Code for every user on a machine. It sets defaults and locks for settings, and limits such as the broadest permission mode or which providers may run. The T3 Code server on the machine enforces it for every app that connects: the desktop app, a browser, or the mobile app.
- Write the policy as JSON, starting from the example.
- Run
t3 policy check policy.json. It prints what the policy will do, or what is wrong with it. - Deploy the policy to each machine with your device management tool, in one of the supported places.
- Restart T3 Code on the machine. The server reads the policy only when it starts.
- Run
t3 policy showon the machine to check what the server loaded.
The desktop app includes the t3 command; see The t3 command.
{
"schemaVersion": 1,
"organization": "Example Corp",
"supportUrl": "https://it.example.com/t3",
"settings": {
"defaultRuntimeMode": { "value": "approval-required", "locked": false },
"enableAgentBrowserAccess": { "value": false, "locked": true }
},
"limits": {
"maxRuntimeMode": "auto-accept-edits",
"clients": ["desktop", "local-browser"],
"telemetry": false
}
}schemaVersion and organization are required. Users see the organization's name in Settings
and beside each control the policy locks, with a link to supportUrl if you set one. settings
sets defaults and locks, and limits restricts what T3 Code may do.
Every user on the machine can read the policy, so do not put credentials in it.
Use one of these per machine:
| OS | Native policy | Policy file |
|---|---|---|
| macOS | Configuration profile for com.t3tools.t3code |
/Library/Application Support/T3Code/policy.json |
| Windows | Group Policy template, or a Policy JSON value (both under HKLM\SOFTWARE\Policies\T3Tools\T3Code) |
C:\Program Files\T3Code\policy.json |
| Linux | /etc/t3code/policy.json |
T3 Code does not merge sources. If a machine has both a native policy and a policy file, T3 Code refuses to start until you remove one.
Policy file. Copy the JSON to the path for the OS. Make it and its folder writable only by administrators.
macOS configuration profile. Add a custom settings payload for the preference domain
com.t3tools.t3code. The payload's keys are the policy's top-level keys (schemaVersion,
organization, supportUrl, settings, limits); JSON objects become dictionaries and lists
become arrays. To make the payload from a policy file you checked, run
plutil -convert xml1 -o com.t3tools.t3code.plist policy.json and upload the result, or run
t3 policy templates <directory> --policy policy.json to write a complete
macos/t3code.mobileconfig. Install it as
a device profile, since T3 Code ignores user profiles, and deploy only one profile for this domain.
A profile cannot hold null, so use a policy file if a setting's value must be null.
Windows Group Policy. Run t3 policy templates <directory> and copy
windows/t3code.admx and windows/en-US/t3code.adml into your PolicyDefinitions folder or
central store, or import them into Intune. The policies appear under Computer Configuration >
Administrative Templates > T3 Code. Set Organization whenever you set any other T3 Code policy.
Locks and provider fields go in the two JSON policies, as the same objects a policy file would hold.
Machines pick up a change at the next Group Policy refresh; run gpupdate /target:computer /force
to apply it now, then restart T3 Code.
Setting Allow T3 Connect, Allow webhook tasks, Allow importing browser cookies, or
Allow usage data to Disabled turns that feature off. Setting any other T3 Code policy to
Disabled restricts nothing, the same as Not Configured.
Windows registry value. Instead of the template, set the Policy value (REG_SZ) to the whole policy as one line of JSON,
from Group Policy Preferences or an Intune script. T3 Code reads it through the Windows registry
API, so it works where registry tools such as reg.exe are blocked for users. Use one or the other:
T3 Code refuses to start when the key holds both a Policy value and the template's settings. In
PowerShell, as an administrator:
$json = (Get-Content -Raw -Encoding UTF8 policy.json) -replace '\r?\n\s*', ''
New-Item -Path HKLM:\SOFTWARE\Policies\T3Tools\T3Code -Force | Out-Null
Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\T3Tools\T3Code -Name Policy -Value $json -Type StringRun t3 policy show as the user who runs T3 Code. It prints the installed policy and its
fingerprint, then says whether the running server loaded that policy or an older one. The server
reads the policy only when it starts, so "Restart T3 Code to apply the installed policy" means it
still enforces the old one. In Settings, users see that your organization manages the environment,
and each locked control shows Managed by.
After you deploy, change, or remove a policy, restart the T3 Code server on the machine: quit and
reopen the desktop app, or run t3 service restart for the
background service. Reconnecting a phone or browser does not reload the
policy.
To remove a policy, delete the file, remove the profile, set the Group Policy settings to Not Configured, or delete the registry value, then restart the server. Users get back the settings they had chosen, since T3 Code never writes policy values into them.
T3 Code refuses to start rather than run with a policy it cannot fully enforce. That includes a policy it cannot read or parse, a setting it does not know, a value of the wrong type, a key this version does not support, or a machine with both a native policy and a file. The error names where the policy came from and what is wrong.
t3 policy check catches the same problems in a JSON file. For a profile or registry value, check
the JSON before you convert it. For the Group Policy template, check a complete policy file that
holds the same settings, then paste its settings and limits.providers.instances objects into the
two JSON policies. A policy written for a newer version can use keys an older one
cannot enforce, so upgrade T3 Code before you deploy a policy that uses them.
Each key under settings is a setting's path in the server's settings.json, such as
storageCleanup.logsAfterDays. To find a path, change the setting in T3 Code and look in
settings.json under ~/.t3/userdata.
"locked": falsesets a default. It applies wherever the user has not chosen a value of their own, and users can change it."locked": truesets the value. Users and agents cannot change it, in Settings or in a project's settings, and Settings shows the control as managed by your organization. Removing the policy brings back the user's own value.
A few settings need care:
- A default cannot add an entry, such as a provider instance, because users could never remove it.
Lock the entry instead, or set provider fields with
providers.instances. - A setting that can be empty, such as
defaultModelSelection, is managed as a whole. You cannot manage one field inside it. defaultRuntimeModecannot be broader thanmaxRuntimeMode.worktreeCleanup, which projects can also set, overrides theworktreerules instorageCleanup. Lock it along with those rules; T3 Code refuses a policy that locks only the rules.- Manage
idleCompaction, not the oldercompactIdleThreads. A Claude instance's own idle compaction choice still wins overidleCompaction. - A provider instance's fields override each other, so lock
providerInstances.<id>as a whole, or pin single fields withproviders.instances. T3 Code refuses a lock on one field inside an instance. A whole-instance lock covers only that instance; to cover every instance of a provider, useproviders.instances.
limits restricts what T3 Code may do. Leave a key out to keep T3 Code's normal behavior.
{
"schemaVersion": 1,
"organization": "Example Corp",
"limits": {
"maxRuntimeMode": "auto-accept-edits",
"clients": ["desktop"],
"connect": false,
"webhookTasks": false,
"browserCookieImport": false,
"telemetry": false,
"providers": {
"allowedDrivers": ["claudeAgent", "codex"],
"instances": {
"claudeAgent": {
"locked": true,
"environment": [{ "name": "CLAUDE_CODE_USE_BEDROCK", "value": "1" }]
}
}
},
"updates": { "mode": "auto", "feedUrl": "https://updates.example.com/t3code/" }
}
}maxRuntimeMode is the broadest permission mode any thread may use:
approval-required (Supervised), auto-accept-edits, auto, or full-access. It covers threads
that users start, agents start, and scheduled tasks start. A thread already above the limit cannot
run another turn until someone lowers its mode; its queued messages send once that happens. New
threads start at the limit when the user's default is above it.
With maxRuntimeMode set, you own each provider's launch settings: binary path, launch arguments,
config home, and the environment variables set on each provider. They come from
providers.instances or T3 Code's defaults, and users cannot change them, because a
wrapper script, a launch flag, or a variable such as NODE_OPTIONS could otherwise turn off the
provider's approvals. Pin the variables your users need, such as a gateway URL, in
providers.instances.
Variables in the environment that starts T3 Code itself still reach providers, and a default
binary path such as claude is looked up on that environment's PATH. The desktop app builds that
environment from settings the user controls, such as their shell profile, so a user could put their
own claude earlier on their PATH. Pin an absolute binaryPath in providers.instances for each
provider you allow.
ACP agents' own mode menus have no effect under maxRuntimeMode, except Antigravity's, whose modes
T3 Code can place under the limit. Users still choose which ACP Registry agent runs; leave
acpRegistry out of allowedDrivers to stop that.
The limit decides which modes T3 Code asks the provider for. It is not a sandbox; see What a policy does not cover.
clients lists how the server may run:
desktop: as the desktop app's own server.local-browser: as a server only browsers on this machine reach, such asnpx t3.remote: reachable from phones, other computers, and app.t3.codes over the network.
remote is the line that matters. A server reachable from the network also accepts browsers on
this machine, and the two local kinds differ only in how T3 Code was started. On machines with the
desktop app, keep desktop in the list, or the app cannot start.
Without remote, the server listens only on a loopback address, creates no pairing links, and
signs out devices that paired earlier. It does not start with a host other than a loopback
address or localhost. A background service installed before
T3CODE_LAN existed keeps private-network access, so without remote it refuses to start.
Run t3 up --no-lan when its service.env sets no T3CODE_HOST, or set
T3CODE_HOST=127.0.0.1 in service.env, then run t3 service restart. The desktop app starts
with network access and Tailscale Serve off, whatever the user chose. That also turns off
T3 Connect. To keep other remote access but turn off T3 Connect alone, set "connect": false.
When it starts, the server also turns off each Tailscale Serve route, HTTPS or TCP, that forwards
to it, such as one t3 pair --tailscale set up, and leaves other routes alone. It does not start
while such a route stays on, or while Tailscale's answer cannot be read; the error names the
command that turns the route off. A route whose target is a name, such as localhost or another
machine on the same port, also stops it, since a name can resolve to this server; change the route
or T3 Code's port. If Tailscale is installed but does not answer, as on a Mac where it was never set up, the
server does not start until Tailscale is set up or removed. It does start when Tailscale is not
installed, not running, or
logged out, since nothing forwards then; a route Tailscale kept returns when Tailscale runs again,
so restart T3 Code after that.
webhookTasks: false: scheduled tasks cannot use webhook triggers. Existing webhook tasks stay but do not run, and cannot be turned back on. Users can still edit or delete them.browserCookieImport: false: users cannot import cookies from another browser into T3 Code's browser.telemetry: false: the server and desktop app send no product usage data or relay connection traces. A policy cannot turn them on. A browser that opens the server, such as app.t3.codes, still sends its own relay traces.
providers.allowedDrivers lists the providers that may run, by ID: codex (Codex),
claudeAgent (Claude), cursor (Cursor), grok (Grok), opencode (OpenCode), antigravity
(Antigravity), fx (fx), kiro (Kiro), pi (Pi), muse (Muse Code), and acpRegistry (ACP
Registry).
t3 policy check rejects an ID this version does not know. Threads on other providers stay,
marked unavailable, and users can still turn those providers off.
providers.instances sets fields on every instance of a provider, such as an environment variable
that routes Claude through Amazon Bedrock or a base URL for a company gateway. binaryPath and
launchArgs work only for providers that have those settings. With "locked": true, users cannot
change the fields you set. With "locked": false, the fields fill in only what the user left
empty. A codex entry that is locked, or any codex entry under maxRuntimeMode, also rules out
Connect with ChatGPT, since that runtime would skip
your fields. In the same way, an opencode entry like that rules out a server URL, so OpenCode
starts the binary you set.
mode:autokeeps the normal update flow.offstops T3 Code from checking for updates, for machines that get T3 Code through device management.feedUrl: T3 Code updates from your own mirror instead of GitHub: the desktop app,t3 update, and updates started from another device. Publish only the versions you have approved, laid out as on a GitHub release. The release's update files (latest.yml,latest-mac.yml,latest-linux.yml,latest-linux-arm64.yml, and theirnightlyversions if you allow nightly) and the desktop installers they list go at the URL's root. The release'st3-*archives andSHA256SUMSgo underv<version>/. At3run throughnpxcomes from npm, so pin it with your package tooling.allowNightly: false: the nightly channel is unavailable.
The policy controls what T3 Code does. Agents run through each provider's own CLI, which reads its
own configuration, such as ~/.claude, and which a user can also run outside T3 Code. To restrict
those CLIs, use their own managed configuration, such as Claude Code's managed settings.
On a managed Windows machine, the desktop app does not run a WSL environment. A T3 Code server a
user starts inside WSL reads /etc/t3code/policy.json in that distro, not the Windows policy.
Deploy the policy file there too if your users run T3 Code in WSL.
Without remote, T3 Code does not expose the server, but a user can still forward its local port
with other tools.
A user with administrator rights on the machine can edit or remove the policy. T3 Code enforces the policy in its own processes, which run as the user, so it stops users from changing managed settings in T3 Code but does not stop a user who modifies or replaces T3 Code itself. A version from before managed policy ignores it too. To control which builds can run, use your device management tool's application controls.