Skip to content

Commit 1ba2f3f

Browse files
committed
Add API documentation for Route Policies
- Add comprehensive Route Policies API documentation - Create, get, list, update, delete endpoints - Complete filtering documentation (guids, route_guids, space_guids, sources, source_guids) - Include parameter documentation (route, app, space, organization, source) - Practical use case examples for common scenarios - Validation rules and permission matrices - Update Domains documentation - Add enforce_route_policies and route_policies_scope fields - Document immutability of enforcement settings - Add example for creating identity-aware domain - Update index.html.md to include route_policies resources Route policies enable identity-aware routing by controlling which Cloud Foundry apps, spaces, or organizations can access routes on domains with enforce_route_policies enabled. GoRouter enforces these access controls using mutual TLS (mTLS).
1 parent ca18462 commit 1ba2f3f

12 files changed

Lines changed: 614 additions & 0 deletions

File tree

docs/v3/source/includes/api_resources/_domains.erb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,37 @@
8787
"href": "https://api.example.org/routing/v1/router_groups/5806148f-cce6-4d86-7fbd-aa269e3f6f3f"
8888
}
8989
}
90+
},
91+
{
92+
"guid": "9b2f3d89-3f89-4f05-8188-8a2b298c79d5",
93+
"created_at": "2026-04-21T10:15:30Z",
94+
"updated_at": "2026-04-21T10:15:30Z",
95+
"name": "apps.identity",
96+
"internal": false,
97+
"router_group": null,
98+
"supported_protocols": ["http"],
99+
"enforce_route_policies": true,
100+
"route_policies_scope": "org",
101+
"metadata": {
102+
"labels": {},
103+
"annotations": {}
104+
},
105+
"relationships": {
106+
"organization": {
107+
"data": null
108+
},
109+
"shared_organizations": {
110+
"data": []
111+
}
112+
},
113+
"links": {
114+
"self": {
115+
"href": "https://api.example.org/v3/domains/9b2f3d89-3f89-4f05-8188-8a2b298c79d5"
116+
},
117+
"route_reservations": {
118+
"href": "https://api.example.org/v3/domains/9b2f3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations"
119+
}
120+
}
90121
}
91122
]
92123
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<% content_for :single_route_policy do | metadata={} | %>
2+
{
3+
"guid": "a4ad8bc1-67a6-4ffa-95b7-f8cf04ad7d4f",
4+
"created_at": "2026-04-21T10:15:30Z",
5+
"updated_at": "2026-04-21T10:15:30Z",
6+
"source": "cf:app:d76446a1-f429-4444-8797-be2f78b75b08",
7+
"metadata": {
8+
"labels": <%= metadata.fetch(:labels, {}).to_json(space: ' ', object_nl: ' ')%>,
9+
"annotations": <%= metadata.fetch(:annotations, {}).to_json(space: ' ', object_nl: ' ')%>
10+
},
11+
"relationships": {
12+
"route": {
13+
"data": {
14+
"guid": "89b32bd6-688f-4424-b94f-2e2c86495a5f"
15+
}
16+
},
17+
"app": {
18+
"data": {
19+
"guid": "d76446a1-f429-4444-8797-be2f78b75b08"
20+
}
21+
},
22+
"space": {
23+
"data": null
24+
},
25+
"organization": {
26+
"data": null
27+
}
28+
},
29+
"links": {
30+
"self": {
31+
"href": "https://api.example.org/v3/route_policies/a4ad8bc1-67a6-4ffa-95b7-f8cf04ad7d4f"
32+
},
33+
"route": {
34+
"href": "https://api.example.org/v3/routes/89b32bd6-688f-4424-b94f-2e2c86495a5f"
35+
}
36+
}
37+
}
38+
<% end %>
39+
40+
<% content_for :paginated_list_of_route_policies do |base_url| %>
41+
{
42+
"pagination": {
43+
"total_results": 3,
44+
"total_pages": 2,
45+
"first": {
46+
"href": "https://api.example.org<%= base_url %>?page=1&per_page=2"
47+
},
48+
"last": {
49+
"href": "https://api.example.org<%= base_url %>?page=2&per_page=2"
50+
},
51+
"next": {
52+
"href": "https://api.example.org<%= base_url %>?page=2&per_page=2"
53+
},
54+
"previous": null
55+
},
56+
"resources": [
57+
{
58+
"guid": "a4ad8bc1-67a6-4ffa-95b7-f8cf04ad7d4f",
59+
"created_at": "2026-04-21T10:15:30Z",
60+
"updated_at": "2026-04-21T10:15:30Z",
61+
"source": "cf:app:d76446a1-f429-4444-8797-be2f78b75b08",
62+
"metadata": {
63+
"labels": {},
64+
"annotations": {}
65+
},
66+
"relationships": {
67+
"route": {
68+
"data": {
69+
"guid": "89b32bd6-688f-4424-b94f-2e2c86495a5f"
70+
}
71+
},
72+
"app": {
73+
"data": {
74+
"guid": "d76446a1-f429-4444-8797-be2f78b75b08"
75+
}
76+
},
77+
"space": {
78+
"data": null
79+
},
80+
"organization": {
81+
"data": null
82+
}
83+
},
84+
"links": {
85+
"self": {
86+
"href": "https://api.example.org/v3/route_policies/a4ad8bc1-67a6-4ffa-95b7-f8cf04ad7d4f"
87+
},
88+
"route": {
89+
"href": "https://api.example.org/v3/routes/89b32bd6-688f-4424-b94f-2e2c86495a5f"
90+
}
91+
}
92+
},
93+
{
94+
"guid": "f2b5d8c3-92a1-4e3f-b847-9c8f1d2e3a4b",
95+
"created_at": "2026-04-21T11:20:45Z",
96+
"updated_at": "2026-04-21T11:20:45Z",
97+
"source": "cf:space:3fa85f64-5717-4562-b3fc-2c963f66afa6",
98+
"metadata": {
99+
"labels": {},
100+
"annotations": {}
101+
},
102+
"relationships": {
103+
"route": {
104+
"data": {
105+
"guid": "89b32bd6-688f-4424-b94f-2e2c86495a5f"
106+
}
107+
},
108+
"app": {
109+
"data": null
110+
},
111+
"space": {
112+
"data": {
113+
"guid": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
114+
}
115+
},
116+
"organization": {
117+
"data": null
118+
}
119+
},
120+
"links": {
121+
"self": {
122+
"href": "https://api.example.org/v3/route_policies/f2b5d8c3-92a1-4e3f-b847-9c8f1d2e3a4b"
123+
},
124+
"route": {
125+
"href": "https://api.example.org/v3/routes/89b32bd6-688f-4424-b94f-2e2c86495a5f"
126+
}
127+
}
128+
}
129+
]
130+
}
131+
<% end %>

docs/v3/source/includes/resources/domains/_create.md.erb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ curl "https://api.example.org/v3/domains" \
1515
}'
1616
```
1717

18+
```
19+
Example Request (Identity-Aware Domain)
20+
```
21+
22+
```shell
23+
curl "https://api.example.org/v3/domains" \
24+
-X POST \
25+
-H "Authorization: bearer [token]" \
26+
-H "Content-type: application/json" \
27+
-d '{
28+
"name": "apps.identity",
29+
"internal": false,
30+
"enforce_route_policies": true,
31+
"route_policies_scope": "org"
32+
}'
33+
```
34+
1835
```
1936
Example Response
2037
```
@@ -41,6 +58,8 @@ Name | Type | Description
4158
| ----------- | -------- | ------------------------------------------------------------------------ | ------- |
4259
| **internal** | _boolean_ | Whether the domain is used for internal (container-to-container) traffic, or external (user-to-container) traffic | false |
4360
| **router_group.guid** | _uuid_ | The desired router group guid. <br>_note: creates a `tcp` domain; cannot be used when `internal` is set to `true` or domain is scoped to an org_ | null |
61+
| **enforce_route_policies** | _boolean_ | When `true`, GoRouter enforces route policies for routes on this domain using mutual TLS (mTLS). **Immutable** after creation. Cannot be used with internal domains | false |
62+
| **route_policies_scope** | _string_ | Operator-defined boundary for allowed callers: `any`, `org`, or `space`. Required when `enforce_route_policies` is `true`. **Immutable** after creation | |
4463
| **organization** | [_to-one relationship_](#to-one-relationships) | A relationship to the organization the domain will be scoped to; <br>_note: cannot be used when `internal` is set to `true` or domain is associated with a router group_ | |
4564
| **shared_organizations** | [_to-many relationship_](#to-many-relationships) | A relationship to organizations the domain will be shared with <br>_Note: cannot be used without an organization relationship_ | |
4665
| **metadata.labels** | [_label object_](#labels) | Labels applied to the domain | |

docs/v3/source/includes/resources/domains/_object.md.erb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Example Domain object
1717
| **internal** | _boolean_ | Whether the domain is used for internal (container-to-container) traffic
1818
| **router_group.guid** | _uuid_ | The guid of the desired router group to route `tcp` traffic through; if set, the domain will only be available for `tcp` traffic
1919
| **supported_protocols** | _list of strings_ | Available protocols for routes using the domain, currently `http` and `tcp`
20+
| **enforce_route_policies** | _boolean_ | When `true`, GoRouter enforces route policies for routes on this domain. This field only appears in the response when set to `true`. **Immutable** after domain creation
21+
| **route_policies_scope** | _string_ | Operator-defined boundary for allowed callers: `any`, `org`, or `space`. Required when `enforce_route_policies` is `true`. This field only appears when `enforce_route_policies` is `true`. **Immutable** after domain creation
2022
| **relationships.organization** | [_to-one relationship_](#to-one-relationships) | The organization the domain is scoped to; if set, the domain will only be available in that organization; otherwise, the domain will be globally available
2123
| **relationships.shared_organizations** | [_to-many relationship_](#to-many-relationships) | Organizations the domain is shared with; if set, the domain will be available in these organizations in addition to the organization the domain is scoped to
2224
| **metadata.labels** | [_label object_](#labels) | Labels applied to the domain
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
### Create a route policy
2+
3+
```
4+
Example Request (Allow specific app)
5+
```
6+
7+
```shell
8+
curl "https://api.example.org/v3/route_policies" \
9+
-X POST \
10+
-H "Authorization: bearer [token]" \
11+
-H "Content-type: application/json" \
12+
-d '{
13+
"source": "cf:app:d76446a1-f429-4444-8797-be2f78b75b08",
14+
"relationships": {
15+
"route": {
16+
"data": {
17+
"guid": "89b32bd6-688f-4424-b94f-2e2c86495a5f"
18+
}
19+
}
20+
},
21+
"metadata": {
22+
"labels": { "team": "frontend" },
23+
"annotations": { "description": "Allow frontend app to call backend API" }
24+
}
25+
}'
26+
```
27+
28+
```
29+
Example Response
30+
```
31+
32+
```http
33+
HTTP/1.1 201 Created
34+
Content-Type: application/json
35+
36+
<%= yield_content :single_route_policy, {
37+
labels: { "team" => "frontend" },
38+
annotations: { "description" => "Allow frontend app to call backend API" }
39+
} %>
40+
```
41+
42+
```
43+
Example Request (Allow all apps in a space)
44+
```
45+
46+
```shell
47+
curl "https://api.example.org/v3/route_policies" \
48+
-X POST \
49+
-H "Authorization: bearer [token]" \
50+
-H "Content-type: application/json" \
51+
-d '{
52+
"source": "cf:space:3fa85f64-5717-4562-b3fc-2c963f66afa6",
53+
"relationships": {
54+
"route": {
55+
"data": {
56+
"guid": "89b32bd6-688f-4424-b94f-2e2c86495a5f"
57+
}
58+
}
59+
}
60+
}'
61+
```
62+
63+
```
64+
Example Request (Allow any caller)
65+
```
66+
67+
```shell
68+
curl "https://api.example.org/v3/route_policies" \
69+
-X POST \
70+
-H "Authorization: bearer [token]" \
71+
-H "Content-type: application/json" \
72+
-d '{
73+
"source": "cf:any",
74+
"relationships": {
75+
"route": {
76+
"data": {
77+
"guid": "89b32bd6-688f-4424-b94f-2e2c86495a5f"
78+
}
79+
}
80+
}
81+
}'
82+
```
83+
84+
#### Definition
85+
`POST /v3/route_policies`
86+
87+
#### Required parameters
88+
89+
| Name | Type | Description
90+
| ----------- | -------- | -----------
91+
| **source** | _string_ | The policy selector. Must be `cf:app:<uuid>`, `cf:space:<uuid>`, `cf:org:<uuid>`, or `cf:any`
92+
| **relationships.route** | [_to-one relationship_](#to-one-relationships) | The route this policy applies to
93+
94+
#### Optional parameters
95+
96+
| Name | Type | Description
97+
| ----------- | -------- | -----------
98+
| **metadata.labels** | [_label object_](#labels) | Labels applied to the route policy
99+
| **metadata.annotations** | [_annotation object_](#annotations) | Annotations applied to the route policy
100+
101+
#### Validation rules
102+
103+
- The route's domain must have `enforce_route_policies` set to `true`
104+
- The route's domain must not be internal (internal routes bypass GoRouter)
105+
- The `source` must be unique per route (duplicate sources are rejected)
106+
- If the route already has a `cf:any` policy, no other sources can be added
107+
- If adding `cf:any`, the route must not have any existing policies
108+
- The source GUID is not validated at creation time (allows cross-org sharing)
109+
110+
#### Permitted roles
111+
112+
Role | Notes
113+
----- | ---
114+
Admin |
115+
Space Developer | Can create policies for routes in spaces they can write to
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
### Delete a route policy
2+
3+
```
4+
Example Request
5+
```
6+
7+
```shell
8+
curl "https://api.example.org/v3/route_policies/a4ad8bc1-67a6-4ffa-95b7-f8cf04ad7d4f" \
9+
-X DELETE \
10+
-H "Authorization: bearer [token]"
11+
```
12+
13+
```
14+
Example Response
15+
```
16+
17+
```http
18+
HTTP/1.1 204 No Content
19+
```
20+
21+
#### Definition
22+
`DELETE /v3/route_policies/:guid`
23+
24+
Deleting a route policy removes the access control for that specific source. If this was the only policy on the route, the route will become inaccessible (no callers will be allowed) until new policies are added or a `cf:any` policy is created.
25+
26+
#### Permitted roles
27+
28+
Role | Notes
29+
----- | ---
30+
Admin |
31+
Space Developer | Can delete policies for routes in spaces they can write to

0 commit comments

Comments
 (0)