Skip to content

Commit dd30c8e

Browse files
authored
chore: refactor network permissions to use explicit domain and unix socket rule maps (openai#15120)
## Summary This PR replaces the legacy network allow/deny list model with explicit rule maps for domains and unix sockets across managed requirements, permissions profiles, the network proxy config, and the app server protocol. Concretely, it: - introduces typed domain (`allow` / `deny`) and unix socket permission (`allow` / `none`) entries instead of separate `allowed_domains`, `denied_domains`, and `allow_unix_sockets` lists - updates config loading, managed requirements merging, and exec-policy overlays to read and upsert rule entries consistently - exposes the new shape through protocol/schema outputs, debug surfaces, and app-server config APIs - rejects the legacy list-based keys and updates docs/tests to reflect the new config format ## Why The previous representation split related network policy across multiple parallel lists, which made merging and overriding rules harder to reason about. Moving to explicit keyed permission maps gives us a single source of truth per host/socket entry, makes allow/deny precedence clearer, and gives protocol consumers access to the full rule state instead of derived projections only. ## Backward Compatibility ### Backward compatible - Managed requirements still accept the legacy `experimental_network.allowed_domains`, `experimental_network.denied_domains`, and `experimental_network.allow_unix_sockets` fields. They are normalized into the new canonical `domains` and `unix_sockets` maps internally. - App-server v2 still deserializes legacy `allowedDomains`, `deniedDomains`, and `allowUnixSockets` payloads, so older clients can continue reading managed network requirements. - App-server v2 responses still populate `allowedDomains`, `deniedDomains`, and `allowUnixSockets` as legacy compatibility views derived from the canonical maps. - `managed_allowed_domains_only` keeps the same behavior after normalization. Legacy managed allowlists still participate in the same enforcement path as canonical `domains` entries. ### Not backward compatible - Permissions profiles under `[permissions.<profile>.network]` no longer accept the legacy list-based keys. Those configs must use the canonical `[domains]` and `[unix_sockets]` tables instead of `allowed_domains`, `denied_domains`, or `allow_unix_sockets`. - Managed `experimental_network` config cannot mix canonical and legacy forms in the same block. For example, `domains` cannot be combined with `allowed_domains` or `denied_domains`, and `unix_sockets` cannot be combined with `allow_unix_sockets`. - The canonical format can express explicit `"none"` entries for unix sockets, but those entries do not round-trip through the legacy compatibility fields because the legacy fields only represent allow/deny lists. ## Testing `/target/debug/codex sandbox macos --log-denials /bin/zsh -c 'curl https://www.example.com' ` gives 200 with config ``` [permissions.workspace.network.domains] "www.example.com" = "allow" ``` and fails when set to deny: `curl: (56) CONNECT tunnel failed, response 403`. Also tested backward compatibility path by verifying that adding the following to `/etc/codex/requirements.toml` works: ``` [experimental_network] allowed_domains = ["www.example.com"] ```
1 parent 21a03f1 commit dd30c8e

37 files changed

+2413
-492
lines changed

codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9203,6 +9203,13 @@
92039203
],
92049204
"type": "string"
92059205
},
9206+
"NetworkDomainPermission": {
9207+
"enum": [
9208+
"allow",
9209+
"deny"
9210+
],
9211+
"type": "string"
9212+
},
92069213
"NetworkRequirements": {
92079214
"properties": {
92089215
"allowLocalBinding": {
@@ -9212,6 +9219,7 @@
92129219
]
92139220
},
92149221
"allowUnixSockets": {
9222+
"description": "Legacy compatibility view derived from `unix_sockets`.",
92159223
"items": {
92169224
"type": "string"
92179225
},
@@ -9227,6 +9235,7 @@
92279235
]
92289236
},
92299237
"allowedDomains": {
9238+
"description": "Legacy compatibility view derived from `domains`.",
92309239
"items": {
92319240
"type": "string"
92329241
},
@@ -9248,6 +9257,7 @@
92489257
]
92499258
},
92509259
"deniedDomains": {
9260+
"description": "Legacy compatibility view derived from `domains`.",
92519261
"items": {
92529262
"type": "string"
92539263
},
@@ -9256,6 +9266,16 @@
92569266
"null"
92579267
]
92589268
},
9269+
"domains": {
9270+
"additionalProperties": {
9271+
"$ref": "#/definitions/v2/NetworkDomainPermission"
9272+
},
9273+
"description": "Canonical network permission map for `experimental_network`.",
9274+
"type": [
9275+
"object",
9276+
"null"
9277+
]
9278+
},
92599279
"enabled": {
92609280
"type": [
92619281
"boolean",
@@ -9270,17 +9290,41 @@
92709290
"null"
92719291
]
92729292
},
9293+
"managedAllowedDomainsOnly": {
9294+
"description": "When true, only managed allowlist entries are respected while managed network enforcement is active.",
9295+
"type": [
9296+
"boolean",
9297+
"null"
9298+
]
9299+
},
92739300
"socksPort": {
92749301
"format": "uint16",
92759302
"minimum": 0.0,
92769303
"type": [
92779304
"integer",
92789305
"null"
92799306
]
9307+
},
9308+
"unixSockets": {
9309+
"additionalProperties": {
9310+
"$ref": "#/definitions/v2/NetworkUnixSocketPermission"
9311+
},
9312+
"description": "Canonical unix socket permission map for `experimental_network`.",
9313+
"type": [
9314+
"object",
9315+
"null"
9316+
]
92809317
}
92819318
},
92829319
"type": "object"
92839320
},
9321+
"NetworkUnixSocketPermission": {
9322+
"enum": [
9323+
"allow",
9324+
"none"
9325+
],
9326+
"type": "string"
9327+
},
92849328
"NonSteerableTurnKind": {
92859329
"enum": [
92869330
"review",

codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6017,6 +6017,13 @@
60176017
],
60186018
"type": "string"
60196019
},
6020+
"NetworkDomainPermission": {
6021+
"enum": [
6022+
"allow",
6023+
"deny"
6024+
],
6025+
"type": "string"
6026+
},
60206027
"NetworkRequirements": {
60216028
"properties": {
60226029
"allowLocalBinding": {
@@ -6026,6 +6033,7 @@
60266033
]
60276034
},
60286035
"allowUnixSockets": {
6036+
"description": "Legacy compatibility view derived from `unix_sockets`.",
60296037
"items": {
60306038
"type": "string"
60316039
},
@@ -6041,6 +6049,7 @@
60416049
]
60426050
},
60436051
"allowedDomains": {
6052+
"description": "Legacy compatibility view derived from `domains`.",
60446053
"items": {
60456054
"type": "string"
60466055
},
@@ -6062,6 +6071,7 @@
60626071
]
60636072
},
60646073
"deniedDomains": {
6074+
"description": "Legacy compatibility view derived from `domains`.",
60656075
"items": {
60666076
"type": "string"
60676077
},
@@ -6070,6 +6080,16 @@
60706080
"null"
60716081
]
60726082
},
6083+
"domains": {
6084+
"additionalProperties": {
6085+
"$ref": "#/definitions/NetworkDomainPermission"
6086+
},
6087+
"description": "Canonical network permission map for `experimental_network`.",
6088+
"type": [
6089+
"object",
6090+
"null"
6091+
]
6092+
},
60736093
"enabled": {
60746094
"type": [
60756095
"boolean",
@@ -6084,17 +6104,41 @@
60846104
"null"
60856105
]
60866106
},
6107+
"managedAllowedDomainsOnly": {
6108+
"description": "When true, only managed allowlist entries are respected while managed network enforcement is active.",
6109+
"type": [
6110+
"boolean",
6111+
"null"
6112+
]
6113+
},
60876114
"socksPort": {
60886115
"format": "uint16",
60896116
"minimum": 0.0,
60906117
"type": [
60916118
"integer",
60926119
"null"
60936120
]
6121+
},
6122+
"unixSockets": {
6123+
"additionalProperties": {
6124+
"$ref": "#/definitions/NetworkUnixSocketPermission"
6125+
},
6126+
"description": "Canonical unix socket permission map for `experimental_network`.",
6127+
"type": [
6128+
"object",
6129+
"null"
6130+
]
60946131
}
60956132
},
60966133
"type": "object"
60976134
},
6135+
"NetworkUnixSocketPermission": {
6136+
"enum": [
6137+
"allow",
6138+
"none"
6139+
],
6140+
"type": "string"
6141+
},
60986142
"NonSteerableTurnKind": {
60996143
"enum": [
61006144
"review",

codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@
102102
},
103103
"type": "object"
104104
},
105+
"NetworkDomainPermission": {
106+
"enum": [
107+
"allow",
108+
"deny"
109+
],
110+
"type": "string"
111+
},
105112
"NetworkRequirements": {
106113
"properties": {
107114
"allowLocalBinding": {
@@ -111,6 +118,7 @@
111118
]
112119
},
113120
"allowUnixSockets": {
121+
"description": "Legacy compatibility view derived from `unix_sockets`.",
114122
"items": {
115123
"type": "string"
116124
},
@@ -126,6 +134,7 @@
126134
]
127135
},
128136
"allowedDomains": {
137+
"description": "Legacy compatibility view derived from `domains`.",
129138
"items": {
130139
"type": "string"
131140
},
@@ -147,6 +156,7 @@
147156
]
148157
},
149158
"deniedDomains": {
159+
"description": "Legacy compatibility view derived from `domains`.",
150160
"items": {
151161
"type": "string"
152162
},
@@ -155,6 +165,16 @@
155165
"null"
156166
]
157167
},
168+
"domains": {
169+
"additionalProperties": {
170+
"$ref": "#/definitions/NetworkDomainPermission"
171+
},
172+
"description": "Canonical network permission map for `experimental_network`.",
173+
"type": [
174+
"object",
175+
"null"
176+
]
177+
},
158178
"enabled": {
159179
"type": [
160180
"boolean",
@@ -169,17 +189,41 @@
169189
"null"
170190
]
171191
},
192+
"managedAllowedDomainsOnly": {
193+
"description": "When true, only managed allowlist entries are respected while managed network enforcement is active.",
194+
"type": [
195+
"boolean",
196+
"null"
197+
]
198+
},
172199
"socksPort": {
173200
"format": "uint16",
174201
"minimum": 0.0,
175202
"type": [
176203
"integer",
177204
"null"
178205
]
206+
},
207+
"unixSockets": {
208+
"additionalProperties": {
209+
"$ref": "#/definitions/NetworkUnixSocketPermission"
210+
},
211+
"description": "Canonical unix socket permission map for `experimental_network`.",
212+
"type": [
213+
"object",
214+
"null"
215+
]
179216
}
180217
},
181218
"type": "object"
182219
},
220+
"NetworkUnixSocketPermission": {
221+
"enum": [
222+
"allow",
223+
"none"
224+
],
225+
"type": "string"
226+
},
183227
"ResidencyRequirement": {
184228
"enum": [
185229
"us"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// GENERATED CODE! DO NOT MODIFY BY HAND!
2+
3+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
4+
5+
export type NetworkDomainPermission = "allow" | "deny";
Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
// GENERATED CODE! DO NOT MODIFY BY HAND!
22

33
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
4+
import type { NetworkDomainPermission } from "./NetworkDomainPermission";
5+
import type { NetworkUnixSocketPermission } from "./NetworkUnixSocketPermission";
46

5-
export type NetworkRequirements = { enabled: boolean | null, httpPort: number | null, socksPort: number | null, allowUpstreamProxy: boolean | null, dangerouslyAllowNonLoopbackProxy: boolean | null, dangerouslyAllowAllUnixSockets: boolean | null, allowedDomains: Array<string> | null, deniedDomains: Array<string> | null, allowUnixSockets: Array<string> | null, allowLocalBinding: boolean | null, };
7+
export type NetworkRequirements = { enabled: boolean | null, httpPort: number | null, socksPort: number | null, allowUpstreamProxy: boolean | null, dangerouslyAllowNonLoopbackProxy: boolean | null, dangerouslyAllowAllUnixSockets: boolean | null,
8+
/**
9+
* Canonical network permission map for `experimental_network`.
10+
*/
11+
domains: { [key in string]?: NetworkDomainPermission } | null,
12+
/**
13+
* When true, only managed allowlist entries are respected while managed
14+
* network enforcement is active.
15+
*/
16+
managedAllowedDomainsOnly: boolean | null,
17+
/**
18+
* Legacy compatibility view derived from `domains`.
19+
*/
20+
allowedDomains: Array<string> | null,
21+
/**
22+
* Legacy compatibility view derived from `domains`.
23+
*/
24+
deniedDomains: Array<string> | null,
25+
/**
26+
* Canonical unix socket permission map for `experimental_network`.
27+
*/
28+
unixSockets: { [key in string]?: NetworkUnixSocketPermission } | null,
29+
/**
30+
* Legacy compatibility view derived from `unix_sockets`.
31+
*/
32+
allowUnixSockets: Array<string> | null, allowLocalBinding: boolean | null, };
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// GENERATED CODE! DO NOT MODIFY BY HAND!
2+
3+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
4+
5+
export type NetworkUnixSocketPermission = "allow" | "none";

codex-rs/app-server-protocol/schema/typescript/v2/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,11 @@ export type { ModelUpgradeInfo } from "./ModelUpgradeInfo";
197197
export type { NetworkAccess } from "./NetworkAccess";
198198
export type { NetworkApprovalContext } from "./NetworkApprovalContext";
199199
export type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol";
200+
export type { NetworkDomainPermission } from "./NetworkDomainPermission";
200201
export type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment";
201202
export type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction";
202203
export type { NetworkRequirements } from "./NetworkRequirements";
204+
export type { NetworkUnixSocketPermission } from "./NetworkUnixSocketPermission";
203205
export type { NonSteerableTurnKind } from "./NonSteerableTurnKind";
204206
export type { OverriddenMetadata } from "./OverriddenMetadata";
205207
export type { PatchApplyStatus } from "./PatchApplyStatus";

0 commit comments

Comments
 (0)