Skip to content
Source
Sign inSign up

managed-policy.md

305 lines16.8 KB

1# Managed policy
2​
3A managed policy lets an administrator configure T3 Code for every user on a machine. It sets
4defaults and locks for settings, and limits such as the broadest permission mode or which
5providers may run. The T3 Code server on the machine enforces it for every app that connects:
6the desktop app, a browser, or the mobile app.
7​
8## Roll out a policy
9​
101. Write the policy as JSON, starting from the [example](#write-a-policy).
112. Run `t3 policy check policy.json`. It prints what the policy will do, or what is wrong with it.
123. Deploy the policy to each machine with your device management tool, in
13 [one of the supported places](#where-the-policy-goes).
144. Restart T3 Code on the machine. The server reads the policy only when it starts.
155. Run `t3 policy show` on the machine to
16 [check what the server loaded](#check-what-the-server-loaded).
17​
18The desktop app includes the `t3` command; see [The `t3` command](./install.md#the-t3-command).
19​
20## Write a policy
21​
22```json
24 "schemaVersion": 1,
25 "organization": "Example Corp",
26 "supportUrl": "https://it.example.com/t3",
27 "settings": {
28 "defaultRuntimeMode": { "value": "approval-required", "locked": false },
29 "enableAgentBrowserAccess": { "value": false, "locked": true }
30 },
31 "limits": {
32 "maxRuntimeMode": "auto-accept-edits",
33 "clients": ["desktop", "local-browser"],
34 "telemetry": false
35 }
37```
38​
39`schemaVersion` and `organization` are required. Users see the organization's name in Settings
40and beside each control the policy locks, with a link to `supportUrl` if you set one. [`settings`](#settings)
41sets defaults and locks, and [`limits`](#limits) restricts what T3 Code may do.
42​
43Every user on the machine can read the policy, so do not put credentials in it.
44​
45## Where the policy goes
46​
47Use one of these per machine:
48​
49| OS | Native policy | Policy file |
50| ------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
51| macOS | Configuration profile for `com.t3tools.t3code` | `/Library/Application Support/T3Code/policy.json` |
52| Windows | Group Policy template, or a `Policy` JSON value (both under `HKLM\SOFTWARE\Policies\T3Tools\T3Code`) | `C:\Program Files\T3Code\policy.json` |
53| Linux | | `/etc/t3code/policy.json` |
54​
55T3 Code does not merge sources. If a machine has both a native policy and a policy file, T3 Code
56refuses to start until you remove one.
57​
58**Policy file.** Copy the JSON to the path for the OS. Make it and its folder writable only by
59administrators.
60​
61**macOS configuration profile.** Add a custom settings payload for the preference domain
62`com.t3tools.t3code`. The payload's keys are the policy's top-level keys (`schemaVersion`,
63`organization`, `supportUrl`, `settings`, `limits`); JSON objects become dictionaries and lists
64become arrays. To make the payload from a policy file you checked, run
65`plutil -convert xml1 -o com.t3tools.t3code.plist policy.json` and upload the result, or run
66`t3 policy templates <directory> --policy policy.json` to write a complete
67`macos/t3code.mobileconfig`. Install it as
68a device profile, since T3 Code ignores user profiles, and deploy only one profile for this domain.
69A profile cannot hold `null`, so use a policy file if a setting's value must be `null`.
70​
71**Windows Group Policy.** Run `t3 policy templates <directory>` and copy
72`windows/t3code.admx` and `windows/en-US/t3code.adml` into your `PolicyDefinitions` folder or
73central store, or import them into Intune. The policies appear under Computer Configuration >
74Administrative Templates > T3 Code. Set **Organization** whenever you set any other T3 Code policy.
75Locks and provider fields go in the two JSON policies, as the same objects a policy file would hold.
76Machines pick up a change at the next Group Policy refresh; run `gpupdate /target:computer /force`
77to apply it now, then restart T3 Code.
78Setting **Allow T3 Connect**, **Allow webhook tasks**, **Allow importing browser cookies**, or
79**Allow usage data** to **Disabled** turns that feature off. Setting any other T3 Code policy to
80**Disabled** restricts nothing, the same as **Not Configured**.
81​
82**Windows registry value.** Instead of the template, set the `Policy` value (`REG_SZ`) to the whole policy as one line of JSON,
83from Group Policy Preferences or an Intune script. T3 Code reads it through the Windows registry
84API, so it works where registry tools such as `reg.exe` are blocked for users. Use one or the other:
85T3 Code refuses to start when the key holds both a `Policy` value and the template's settings. In
86PowerShell, as an administrator:
87​
88```powershell
89$json = (Get-Content -Raw -Encoding UTF8 policy.json) -replace '\r?\n\s*', ''
90New-Item -Path HKLM:\SOFTWARE\Policies\T3Tools\T3Code -Force | Out-Null
91Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\T3Tools\T3Code -Name Policy -Value $json -Type String
92```
93​
94## Check what the server loaded
95​
96Run `t3 policy show` as the user who runs T3 Code. It prints the installed policy and its
97fingerprint, then says whether the running server loaded that policy or an older one. The server
98reads the policy only when it starts, so "Restart T3 Code to apply the installed policy" means it
99still enforces the old one. In Settings, users see that your organization manages the environment,
100and each locked control shows **Managed by**.
101​
102## Change or remove a policy
103​
104After you deploy, change, or remove a policy, restart the T3 Code server on the machine: quit and
105reopen the desktop app, or run `t3 service restart` for the
106[background service](./background-service.md). Reconnecting a phone or browser does not reload the
107policy.
108​
109To remove a policy, delete the file, remove the profile, set the Group Policy settings to
110**Not Configured**, or delete the registry value, then restart
111the server. Users get back the settings they had chosen, since T3 Code never writes policy values
112into them.
113​
114## When T3 Code refuses to start
115​
116T3 Code refuses to start rather than run with a policy it cannot fully enforce. That includes a
117policy it cannot read or parse, a setting it does not know, a value of the wrong type, a key this
118version does not support, or a machine with both a native policy and a file. The error names where
119the policy came from and what is wrong.
120​
121`t3 policy check` catches the same problems in a JSON file. For a profile or registry value, check
122the JSON before you convert it. For the Group Policy template, check a complete policy file that
123holds the same settings, then paste its `settings` and `limits.providers.instances` objects into the
124two JSON policies. A policy written for a newer version can use keys an older one
125cannot enforce, so upgrade T3 Code before you deploy a policy that uses them.
126​
127## Settings
128​
129Each key under `settings` is a setting's path in the server's `settings.json`, such as
130`storageCleanup.logsAfterDays`. To find a path, change the setting in T3 Code and look in
131`settings.json` under `~/.t3/userdata`.
132​
133- `"locked": false` sets a default. It applies wherever the user has not chosen a value of their
134 own, and users can change it.
135- `"locked": true` sets the value. Users and agents cannot change it, in Settings or in a project's
136 settings, and Settings shows the control as managed by your organization. Removing the policy
137 brings back the user's own value.
138​
139A few settings need care:
140​
141- A default cannot add an entry, such as a provider instance, because users could never remove it.
142 Lock the entry instead, or set provider fields with [`providers.instances`](#providers).
143- A setting that can be empty, such as `defaultModelSelection`, is managed as a whole. You cannot
144 manage one field inside it.
145- `defaultRuntimeMode` cannot be broader than [`maxRuntimeMode`](#permission-modes).
146- `worktreeCleanup`, which projects can also set, overrides the `worktree` rules in
147 `storageCleanup`. Lock it along with those rules; T3 Code refuses a policy that locks only the
148 rules.
149- `observability` cannot be managed. Its OTLP endpoints come from each user's environment
150 variables, settings file and desktop startup input, which a policy cannot override.
151- Manage `idleCompaction`, not the older `compactIdleThreads`. A Claude instance's own idle
152 compaction choice still wins over `idleCompaction`.
153- A provider instance's fields override each other, so lock `providerInstances.<id>` as a whole,
154 or pin single fields with [`providers.instances`](#providers). T3 Code refuses a lock on one
155 field inside an instance. A whole-instance lock covers only that instance; to cover every
156 instance of a provider, use `providers.instances`.
157​
158## Limits
159​
160`limits` restricts what T3 Code may do. Leave a key out to keep T3 Code's normal behavior.
161​
162```json
164 "schemaVersion": 1,
165 "organization": "Example Corp",
166 "limits": {
167 "maxRuntimeMode": "auto-accept-edits",
168 "clients": ["desktop"],
169 "connect": false,
170 "webhookTasks": false,
171 "browserCookieImport": false,
172 "telemetry": false,
173 "providers": {
174 "allowedDrivers": ["claudeAgent", "codex"],
175 "instances": {
176 "claudeAgent": {
177 "locked": true,
178 "environment": [{ "name": "CLAUDE_CODE_USE_BEDROCK", "value": "1" }]
179 }
180 }
181 },
182 "updates": { "mode": "auto", "feedUrl": "https://updates.example.com/t3code/" }
183 }
185```
186​
187### Permission modes
188​
189`maxRuntimeMode` is the broadest [permission mode](./permission-modes.md) any thread may use:
190`approval-required` (Supervised), `auto-accept-edits`, `auto`, or `full-access`. It covers threads
191that users start, agents start, and scheduled tasks start. A thread already above the limit cannot
192run another turn until someone lowers its mode; its queued messages send once that happens. New
193threads start at the limit when the user's default is above it.
194​
195With `maxRuntimeMode` set, you own each provider's launch settings: binary path, launch arguments,
196config home, and the environment variables set on each provider. They come from
197[`providers.instances`](#providers) or T3 Code's defaults, and users cannot change them, because a
198wrapper script, a launch flag, or a variable such as `NODE_OPTIONS` could otherwise turn off the
199provider's approvals. Pin the variables your users need, such as a gateway URL, in
200`providers.instances`.
201​
202Variables in the environment that starts T3 Code itself still reach providers, and a default
203binary path such as `claude` is looked up on that environment's `PATH`. The desktop app builds that
204environment from settings the user controls, such as their shell profile, so a user could put their
205own `claude` earlier on their `PATH`. Pin an absolute `binaryPath` in `providers.instances` for each
206provider you allow.
207​
208ACP agents' own mode menus have no effect under `maxRuntimeMode`, except Antigravity's, whose modes
209T3 Code can place under the limit. Users still choose which ACP Registry agent runs; leave
210`acpRegistry` out of `allowedDrivers` to stop that.
211​
212The limit decides which modes T3 Code asks the provider for. It is not a sandbox; see
213[What a policy does not cover](#what-a-policy-does-not-cover).
214​
215### Clients and T3 Connect
216​
217`clients` lists how the server may run:
218​
219- `desktop`: as the desktop app's own server.
220- `local-browser`: as a server only browsers on this machine reach, such as `npx t3`.
221- `remote`: reachable from phones, other computers, and app.t3.codes over the network.
222​
223`remote` is the line that matters. A server reachable from the network also accepts browsers on
224this machine, and the two local kinds differ only in how T3 Code was started. On machines with the
225desktop app, keep `desktop` in the list, or the app cannot start.
226​
227Without `remote`, the server listens only on a loopback address, creates no pairing links, and
228signs out devices that paired earlier. It does not start with a host other than a loopback
229address or `localhost`. A [background service](./background-service.md) installed before
230`T3CODE_LAN` existed keeps private-network access, so without `remote` it refuses to start.
231Run `t3 up --no-lan` when its `service.env` sets no `T3CODE_HOST`, or set
232`T3CODE_HOST=127.0.0.1` in `service.env`, then run `t3 service restart`. The desktop app starts
233with network access and Tailscale Serve off, whatever the user chose. That also turns off
234T3 Connect. To keep other remote access but turn off T3 Connect alone, set `"connect": false`.
235​
236When it starts, the server also turns off each Tailscale Serve route, HTTPS or TCP, that forwards
237to it, such as one `t3 pair --tailscale` set up, and leaves other routes alone. It does not start
238while such a route stays on, or while Tailscale's answer cannot be read; the error names the
239command that turns the route off. A route whose target is a name, such as `localhost` or another
240machine on the same port, also stops it, since a name can resolve to this server; change the route
241or T3 Code's port. If Tailscale is installed but does not answer, as on a Mac where it was never set up, the
242server does not start until Tailscale is set up or removed. It does start when Tailscale is not
243installed, not running, or
244logged out, since nothing forwards then; a route Tailscale kept returns when Tailscale runs again,
245so restart T3 Code after that.
246​
247### Other capabilities
248​
249- `webhookTasks: false`: scheduled tasks cannot use webhook triggers. Existing webhook tasks stay
250 but do not run, and cannot be turned back on. Users can still edit or delete them.
251- `browserCookieImport: false`: users cannot import cookies from another browser into T3 Code's
252 browser.
253- `telemetry: false`: the server and desktop app send no [product usage data](./telemetry.md) or
254 relay connection traces. A policy cannot turn them on. A browser that opens the server, such as
255 app.t3.codes, still sends its own relay traces.
256​
257### Providers
258​
259`providers.allowedDrivers` lists the providers that may run, by ID: `codex` (Codex),
260`claudeAgent` (Claude), `cursor` (Cursor), `grok` (Grok), `opencode` (OpenCode), `antigravity`
261(Antigravity), `fx` (fx), `kiro` (Kiro), `pi` (Pi), `muse` (Muse Code), and `acpRegistry` (ACP
262Registry).
263`t3 policy check` rejects an ID this version does not know. Threads on other providers stay,
264marked unavailable, and users can still turn those providers off.
265​
266`providers.instances` sets fields on every instance of a provider, such as an environment variable
267that routes Claude through Amazon Bedrock or a base URL for a company gateway. `binaryPath` and
268`launchArgs` work only for providers that have those settings. With `"locked": true`, users cannot
269change the fields you set. With `"locked": false`, the fields fill in only what the user left
270empty. A `codex` entry that is locked, or any `codex` entry under `maxRuntimeMode`, also rules out
271[Connect with ChatGPT](./providers-codex.md#connect-with-chatgpt), since that runtime would skip
272your fields. In the same way, an `opencode` entry like that rules out a server URL, so OpenCode
273starts the binary you set.
274​
275### Updates
276​
277- `mode`: `auto` keeps the normal update flow. `off` stops T3 Code from checking for updates, for
278 machines that get T3 Code through device management.
279- `feedUrl`: T3 Code updates from your own mirror instead of GitHub: the desktop app, `t3 update`,
280 and updates started from another device. Publish only the versions you have approved, laid out
281 as on a GitHub release. The release's update files (`latest.yml`, `latest-mac.yml`,
282 `latest-linux.yml`, `latest-linux-arm64.yml`, and their `nightly` versions if you allow nightly)
283 and the desktop installers they list go at the URL's root. The release's `t3-*` archives and
284 `SHA256SUMS` go under `v<version>/`. A `t3` run through `npx` comes from npm, so pin it with your
285 package tooling.
286- `allowNightly: false`: the nightly channel is unavailable.
287​
288## What a policy does not cover
289​
290The policy controls what T3 Code does. Agents run through each provider's own CLI, which reads its
291own configuration, such as `~/.claude`, and which a user can also run outside T3 Code. To restrict
292those CLIs, use their own managed configuration, such as Claude Code's managed settings.
293​
294On a managed Windows machine, the desktop app does not run a WSL environment. A T3 Code server a
295user starts inside WSL reads `/etc/t3code/policy.json` in that distro, not the Windows policy.
296Deploy the policy file there too if your users run T3 Code in WSL.
297​
298Without `remote`, T3 Code does not expose the server, but a user can still forward its local port
299with other tools.
300​
301A user with administrator rights on the machine can edit or remove the policy. T3 Code enforces
302the policy in its own processes, which run as the user, so it stops users from changing managed
303settings in T3 Code but does not stop a user who modifies or replaces T3 Code itself. A version
304from before managed policy ignores it too. To control which builds can run, use your device
305management tool's application controls.