forktex-cloud 0.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1057 @@
1
+ # Copyright (C) 2026 FORKTEX S.R.L.
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-ForkTex-Commercial
4
+ #
5
+ # This file is part of forktex-cloud.
6
+ #
7
+ # For commercial licensing -- including use in proprietary products, SaaS
8
+ # deployments, or any context where AGPL obligations cannot be met -- you
9
+ # MUST obtain a commercial license from FORKTEX S.R.L. (info@forktex.com).
10
+ #
11
+ # This program is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU Affero General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU Affero General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU Affero General Public License
22
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+
24
+ """Auto-generated Pydantic models from the ForkTex Cloud API OpenAPI spec.
25
+
26
+ DO NOT EDIT — regenerate with: make codegen
27
+
28
+ Source: GET /api/openapi.json
29
+ API version: 0.6.0
30
+ Spec hash: c9e218a6fef2b1b4
31
+ """
32
+ from __future__ import annotations
33
+
34
+
35
+ SPEC_VERSION = "0.6.0"
36
+ SPEC_HASH = "c9e218a6fef2b1b4"
37
+
38
+
39
+ from enum import StrEnum
40
+ from typing import Annotated, Any, Literal
41
+ from uuid import UUID
42
+
43
+ from pydantic import AwareDatetime, BaseModel, ConfigDict, EmailStr, Field
44
+
45
+
46
+ class AccessRequestCreate(BaseModel):
47
+ company: Annotated[str | None, Field(title="Company")] = None
48
+ email: Annotated[EmailStr, Field(title="Email")]
49
+ name: Annotated[str | None, Field(title="Name")] = None
50
+ use_case: Annotated[str | None, Field(title="Use Case")] = None
51
+
52
+
53
+ class AccessRequestRead(BaseModel):
54
+ company: Annotated[str | None, Field(title="Company")]
55
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
56
+ email: Annotated[str, Field(title="Email")]
57
+ id: Annotated[UUID, Field(title="Id")]
58
+ name: Annotated[str | None, Field(title="Name")]
59
+ status: Annotated[str, Field(title="Status")]
60
+ use_case: Annotated[str | None, Field(title="Use Case")]
61
+
62
+
63
+ class ApiKeyCreate(BaseModel):
64
+ expires_at: Annotated[AwareDatetime | None, Field(title="Expires At")] = None
65
+ label: Annotated[str, Field(title="Label")]
66
+ rate_limit_rpm: Annotated[int | None, Field(title="Rate Limit Rpm")] = None
67
+ scopes: Annotated[list[str] | None, Field(title="Scopes")] = None
68
+
69
+
70
+ class ApiKeyCreated(BaseModel):
71
+ """
72
+ Returned only at creation time — includes the raw key.
73
+ """
74
+
75
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
76
+ expires_at: Annotated[AwareDatetime | None, Field(title="Expires At")] = None
77
+ id: Annotated[UUID, Field(title="Id")]
78
+ is_active: Annotated[bool, Field(title="Is Active")]
79
+ label: Annotated[str, Field(title="Label")]
80
+ last_used_at: Annotated[AwareDatetime | None, Field(title="Last Used At")] = None
81
+ org_id: Annotated[UUID, Field(title="Org Id")]
82
+ rate_limit_rpm: Annotated[int | None, Field(title="Rate Limit Rpm")] = None
83
+ raw_key: Annotated[str, Field(title="Raw Key")]
84
+ scopes: Annotated[list[str] | None, Field(title="Scopes")] = None
85
+
86
+
87
+ class ApiKeyRead(BaseModel):
88
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
89
+ expires_at: Annotated[AwareDatetime | None, Field(title="Expires At")] = None
90
+ id: Annotated[UUID, Field(title="Id")]
91
+ is_active: Annotated[bool, Field(title="Is Active")]
92
+ label: Annotated[str, Field(title="Label")]
93
+ last_used_at: Annotated[AwareDatetime | None, Field(title="Last Used At")] = None
94
+ org_id: Annotated[UUID, Field(title="Org Id")]
95
+ rate_limit_rpm: Annotated[int | None, Field(title="Rate Limit Rpm")] = None
96
+ scopes: Annotated[list[str] | None, Field(title="Scopes")] = None
97
+
98
+
99
+ class ArchitectureSnapshot(BaseModel):
100
+ """
101
+ A C4 architecture snapshot pushed by the forktex CLI.
102
+ """
103
+
104
+ description: Annotated[str | None, Field(title="Description")] = ""
105
+ external_systems: Annotated[
106
+ list[dict[str, Any]] | None, Field(title="External Systems")
107
+ ] = None
108
+ generated_at: Annotated[str, Field(title="Generated At")]
109
+ metadata: Annotated[dict[str, Any] | None, Field(title="Metadata")] = None
110
+ name: Annotated[str, Field(title="Name")]
111
+ relationships: Annotated[
112
+ list[dict[str, Any]] | None, Field(title="Relationships")
113
+ ] = None
114
+ systems: Annotated[list[dict[str, Any]] | None, Field(title="Systems")] = None
115
+
116
+
117
+ class AuditEventRead(BaseModel):
118
+ action: Annotated[str, Field(title="Action")]
119
+ api_key_id: Annotated[UUID | None, Field(title="Api Key Id")] = None
120
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
121
+ details: Annotated[str | None, Field(title="Details")] = None
122
+ id: Annotated[UUID, Field(title="Id")]
123
+ ip_address: Annotated[str | None, Field(title="Ip Address")] = None
124
+ org_id: Annotated[UUID, Field(title="Org Id")]
125
+ resource_id: Annotated[str | None, Field(title="Resource Id")] = None
126
+ resource_type: Annotated[str | None, Field(title="Resource Type")] = None
127
+ user_id: Annotated[UUID | None, Field(title="User Id")] = None
128
+
129
+
130
+ class BackupCreate(BaseModel):
131
+ backup_type: Annotated[str | None, Field(title="Backup Type")] = "full"
132
+ service_id: Annotated[str | None, Field(title="Service Id")] = None
133
+
134
+
135
+ class BackupPolicyDef(BaseModel):
136
+ """
137
+ Backup schedule + retention for persistence services.
138
+ """
139
+
140
+ model_config = ConfigDict(
141
+ extra="forbid",
142
+ )
143
+ enabled: Annotated[bool | None, Field(title="Enabled")] = False
144
+ retentionDays: Annotated[int | None, Field(title="Retentiondays")] = 7
145
+ schedule: Annotated[str | None, Field(title="Schedule")] = "0 3 * * *"
146
+
147
+
148
+ class BackupRead(BaseModel):
149
+ backup_type: Annotated[str, Field(title="Backup Type")]
150
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
151
+ environment_id: Annotated[UUID, Field(title="Environment Id")]
152
+ expires_at: Annotated[AwareDatetime | None, Field(title="Expires At")] = None
153
+ id: Annotated[UUID, Field(title="Id")]
154
+ server_id: Annotated[UUID | None, Field(title="Server Id")] = None
155
+ service_id: Annotated[str | None, Field(title="Service Id")] = None
156
+ size_bytes: Annotated[int | None, Field(title="Size Bytes")] = None
157
+ status: Annotated[str, Field(title="Status")]
158
+ storage_path: Annotated[str | None, Field(title="Storage Path")] = None
159
+
160
+
161
+ class BackupRestoreRead(BaseModel):
162
+ backup_id: Annotated[UUID, Field(title="Backup Id")]
163
+ service_id: Annotated[str, Field(title="Service Id")]
164
+ status: Annotated[str, Field(title="Status")]
165
+
166
+
167
+ class CapacityOrgLimits(BaseModel):
168
+ max_projects: Annotated[int, Field(title="Max Projects")]
169
+ max_servers: Annotated[int, Field(title="Max Servers")]
170
+
171
+
172
+ class CapacityServers(BaseModel):
173
+ limit: Annotated[int, Field(title="Limit")]
174
+ used: Annotated[int, Field(title="Used")]
175
+
176
+
177
+ class ContainerStatus(BaseModel):
178
+ """
179
+ Container state info from Docker inspect.
180
+ """
181
+
182
+ name: Annotated[str, Field(title="Name")]
183
+ state: Annotated[str, Field(title="State")]
184
+ status: Annotated[str, Field(title="Status")]
185
+
186
+
187
+ class DeployRead(BaseModel):
188
+ details: Annotated[str | None, Field(title="Details")] = None
189
+ environment_id: Annotated[UUID | None, Field(title="Environment Id")] = None
190
+ finished_at: Annotated[AwareDatetime | None, Field(title="Finished At")] = None
191
+ id: Annotated[UUID, Field(title="Id")]
192
+ manifest_version_id: Annotated[UUID | None, Field(title="Manifest Version Id")] = (
193
+ None
194
+ )
195
+ project_id: Annotated[UUID | None, Field(title="Project Id")] = None
196
+ source: Annotated[str, Field(title="Source")]
197
+ started_at: Annotated[AwareDatetime, Field(title="Started At")]
198
+ status: Annotated[str, Field(title="Status")]
199
+ tags: Annotated[Any, Field(title="Tags")] = None
200
+
201
+
202
+ class DeploymentDef(BaseModel):
203
+ model_config = ConfigDict(
204
+ extra="forbid",
205
+ )
206
+ drainDelaySeconds: Annotated[int | None, Field(title="Draindelayseconds")] = 5
207
+ gracefulStopSeconds: Annotated[int | None, Field(title="Gracefulstopseconds")] = 30
208
+ healthRetries: Annotated[int | None, Field(title="Healthretries")] = 30
209
+ imageStrategy: Annotated[str | None, Field(title="Imagestrategy")] = None
210
+ registry: Annotated[dict[str, Any] | None, Field(title="Registry")] = None
211
+ strategy: Annotated[Literal["blue-green"], Field(title="Strategy")] = "blue-green"
212
+
213
+
214
+ class DeploymentLogRead(BaseModel):
215
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
216
+ deployment_id: Annotated[UUID, Field(title="Deployment Id")]
217
+ exit_code: Annotated[int | None, Field(title="Exit Code")] = None
218
+ id: Annotated[UUID, Field(title="Id")]
219
+ output: Annotated[str | None, Field(title="Output")] = None
220
+ server_id: Annotated[UUID | None, Field(title="Server Id")] = None
221
+ stage: Annotated[str, Field(title="Stage")]
222
+
223
+
224
+ class EnvironmentCreate(BaseModel):
225
+ is_production: Annotated[bool | None, Field(title="Is Production")] = False
226
+ name: Annotated[str, Field(title="Name")]
227
+
228
+
229
+ class EnvironmentPromotionRequest(BaseModel):
230
+ target_name: Annotated[str | None, Field(title="Target Name")] = "production"
231
+
232
+
233
+ class EnvironmentRead(BaseModel):
234
+ controller_instance: Annotated[str | None, Field(title="Controller Instance")] = (
235
+ None
236
+ )
237
+ controller_url: Annotated[str | None, Field(title="Controller Url")] = None
238
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
239
+ handoff_completed_at: Annotated[
240
+ AwareDatetime | None, Field(title="Handoff Completed At")
241
+ ] = None
242
+ handoff_ready_at: Annotated[
243
+ AwareDatetime | None, Field(title="Handoff Ready At")
244
+ ] = None
245
+ id: Annotated[UUID, Field(title="Id")]
246
+ is_production: Annotated[bool, Field(title="Is Production")]
247
+ name: Annotated[str, Field(title="Name")]
248
+ project_id: Annotated[UUID, Field(title="Project Id")]
249
+ status: Annotated[str, Field(title="Status")]
250
+ updated_at: Annotated[AwareDatetime, Field(title="Updated At")]
251
+
252
+
253
+ class EnvironmentServerCreate(BaseModel):
254
+ role: Annotated[str | None, Field(title="Role")] = "primary"
255
+ server_id: Annotated[UUID, Field(title="Server Id")]
256
+
257
+
258
+ class EnvironmentServerRead(BaseModel):
259
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
260
+ environment_id: Annotated[UUID, Field(title="Environment Id")]
261
+ id: Annotated[UUID, Field(title="Id")]
262
+ role: Annotated[str, Field(title="Role")]
263
+ server_id: Annotated[UUID, Field(title="Server Id")]
264
+
265
+
266
+ class EnvironmentServiceRead(BaseModel):
267
+ active_slot: Annotated[str | None, Field(title="Active Slot")] = None
268
+ current_image: Annotated[str | None, Field(title="Current Image")] = None
269
+ environment_id: Annotated[UUID, Field(title="Environment Id")]
270
+ id: Annotated[UUID, Field(title="Id")]
271
+ service_id: Annotated[str, Field(title="Service Id")]
272
+ service_type: Annotated[str, Field(title="Service Type")]
273
+ status: Annotated[str | None, Field(title="Status")] = None
274
+
275
+
276
+ class EnvironmentStateExportRead(BaseModel):
277
+ backups: Annotated[list[dict[str, Any]], Field(title="Backups")]
278
+ environment: Annotated[dict[str, Any], Field(title="Environment")]
279
+ manifests: Annotated[list[dict[str, Any]], Field(title="Manifests")]
280
+ services: Annotated[list[dict[str, Any]], Field(title="Services")]
281
+ vault_entries: Annotated[list[dict[str, Any]], Field(title="Vault Entries")]
282
+
283
+
284
+ class EnvironmentStateImportRequest(BaseModel):
285
+ state: Annotated[dict[str, Any], Field(title="State")]
286
+
287
+
288
+ class EventRead(BaseModel):
289
+ action: Annotated[str, Field(title="Action")]
290
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
291
+ details: Annotated[str | None, Field(title="Details")] = None
292
+ environment_id: Annotated[UUID | None, Field(title="Environment Id")] = None
293
+ id: Annotated[int, Field(title="Id")]
294
+ org_id: Annotated[UUID | None, Field(title="Org Id")] = None
295
+ project_id: Annotated[UUID | None, Field(title="Project Id")] = None
296
+ server_id: Annotated[UUID | None, Field(title="Server Id")] = None
297
+ status: Annotated[str, Field(title="Status")]
298
+
299
+
300
+ class ExecBody(BaseModel):
301
+ command: Annotated[str, Field(title="Command")]
302
+ service: Annotated[str, Field(title="Service")]
303
+
304
+
305
+ class ForgotPasswordRequest(BaseModel):
306
+ email: Annotated[EmailStr, Field(title="Email")]
307
+
308
+
309
+ class GatewayDomain(BaseModel):
310
+ model_config = ConfigDict(
311
+ extra="forbid",
312
+ )
313
+ host: Annotated[str, Field(min_length=1, title="Host")]
314
+ primary: Annotated[bool | None, Field(title="Primary")] = False
315
+
316
+
317
+ class HandoffCriterionRead(BaseModel):
318
+ detail: Annotated[str, Field(title="Detail")]
319
+ name: Annotated[str, Field(title="Name")]
320
+ status: Annotated[bool, Field(title="Status")]
321
+
322
+
323
+ class HandoffReadinessRead(BaseModel):
324
+ controller_instance: Annotated[str | None, Field(title="Controller Instance")] = (
325
+ None
326
+ )
327
+ controller_url: Annotated[str | None, Field(title="Controller Url")] = None
328
+ criteria: Annotated[list[HandoffCriterionRead], Field(title="Criteria")]
329
+ handoff_completed_at: Annotated[
330
+ AwareDatetime | None, Field(title="Handoff Completed At")
331
+ ] = None
332
+ handoff_ready_at: Annotated[
333
+ AwareDatetime | None, Field(title="Handoff Ready At")
334
+ ] = None
335
+ ready: Annotated[bool, Field(title="Ready")]
336
+
337
+
338
+ class InfrastructureDef(BaseModel):
339
+ model_config = ConfigDict(
340
+ extra="forbid",
341
+ )
342
+ flavour: Annotated[str, Field(min_length=1, title="Flavour")]
343
+ image: Annotated[str | None, Field(title="Image")] = "ubuntu-24.04"
344
+ provider: Annotated[str, Field(min_length=1, title="Provider")]
345
+ region: Annotated[str, Field(min_length=1, title="Region")]
346
+
347
+
348
+ class JobResponse(BaseModel):
349
+ """
350
+ Response for asynchronous pipeline operations (/up, /down, /deploy).
351
+ """
352
+
353
+ deployment_id: Annotated[str | None, Field(title="Deployment Id")] = None
354
+ job_id: Annotated[str, Field(title="Job Id")]
355
+ status: Annotated[str, Field(title="Status")]
356
+
357
+
358
+ class LifecycleDef(BaseModel):
359
+ """
360
+ Hooks that run at specific deployment phases.
361
+
362
+ - ``initCommand`` — first deployment only (e.g. ``createdb``)
363
+ - ``preDeployCommand`` — before each deploy (e.g. ``pg_dump > backup.sql``)
364
+ - ``postDeployCommand`` — after each deploy (e.g. ``alembic upgrade head``)
365
+ """
366
+
367
+ model_config = ConfigDict(
368
+ extra="forbid",
369
+ )
370
+ initCommand: Annotated[str | None, Field(title="Initcommand")] = None
371
+ postDeployCommand: Annotated[str | None, Field(title="Postdeploycommand")] = None
372
+ preDeployCommand: Annotated[str | None, Field(title="Predeploycommand")] = None
373
+
374
+
375
+ class LoginRequest(BaseModel):
376
+ email: Annotated[EmailStr, Field(title="Email")]
377
+ password: Annotated[str, Field(title="Password")]
378
+
379
+
380
+ class LokiDef(BaseModel):
381
+ """
382
+ Per-project Loki log aggregator config.
383
+ """
384
+
385
+ model_config = ConfigDict(
386
+ extra="forbid",
387
+ )
388
+ enabled: Annotated[bool | None, Field(title="Enabled")] = True
389
+ retentionDays: Annotated[int | None, Field(title="Retentiondays")] = 14
390
+
391
+
392
+ class MCPInfo(BaseModel):
393
+ description: Annotated[str, Field(title="Description")]
394
+ """
395
+ Human-readable summary
396
+ """
397
+ endpoint: Annotated[str, Field(title="Endpoint")]
398
+ """
399
+ HTTP path of the MCP JSON-RPC transport
400
+ """
401
+ protocol: Annotated[str, Field(title="Protocol")]
402
+ """
403
+ Protocol identifier
404
+ """
405
+ spec_url: Annotated[str, Field(title="Spec Url")]
406
+ """
407
+ Link to the MCP specification
408
+ """
409
+ version: Annotated[str, Field(title="Version")]
410
+ """
411
+ MCP protocol version this server targets
412
+ """
413
+
414
+
415
+ class ManifestPush(BaseModel):
416
+ manifest_json: Annotated[dict[str, Any], Field(title="Manifest Json")]
417
+
418
+
419
+ class ManifestVersionRead(BaseModel):
420
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
421
+ deployed_at: Annotated[AwareDatetime | None, Field(title="Deployed At")] = None
422
+ environment_id: Annotated[UUID, Field(title="Environment Id")]
423
+ id: Annotated[UUID, Field(title="Id")]
424
+ manifest_hash: Annotated[str, Field(title="Manifest Hash")]
425
+ manifest_json: Annotated[dict[str, Any], Field(title="Manifest Json")]
426
+ version: Annotated[int, Field(title="Version")]
427
+
428
+
429
+ class MetadataDef(BaseModel):
430
+ model_config = ConfigDict(
431
+ extra="forbid",
432
+ )
433
+ environment: Annotated[str | None, Field(title="Environment")] = None
434
+ local: Annotated[dict[str, Any] | None, Field(title="Local")] = None
435
+ name: Annotated[str, Field(min_length=1, title="Name")]
436
+ projectId: Annotated[str | None, Field(title="Projectid")] = None
437
+
438
+
439
+ class MetricsDef(BaseModel):
440
+ """
441
+ Metrics subsystem — Prometheus scrapes /metrics endpoints.
442
+ """
443
+
444
+ model_config = ConfigDict(
445
+ extra="forbid",
446
+ )
447
+ enabled: Annotated[bool | None, Field(title="Enabled")] = True
448
+ retentionDays: Annotated[int | None, Field(title="Retentiondays")] = 7
449
+
450
+
451
+ class NativeBuildSpec(BaseModel):
452
+ model_config = ConfigDict(
453
+ extra="forbid",
454
+ )
455
+ command: Annotated[str, Field(min_length=1, title="Command")]
456
+ outputBinary: Annotated[str, Field(min_length=1, title="Outputbinary")]
457
+
458
+
459
+ class OpsResponse(BaseModel):
460
+ """
461
+ Response for server operations (restart, exec, switch, update).
462
+ """
463
+
464
+ output: Annotated[str | None, Field(title="Output")] = ""
465
+ status: Annotated[str, Field(title="Status")]
466
+
467
+
468
+ class OrgBrief(BaseModel):
469
+ id: Annotated[UUID, Field(title="Id")]
470
+ name: Annotated[str, Field(title="Name")]
471
+ slug: Annotated[str, Field(title="Slug")]
472
+
473
+
474
+ class OrgCreate(BaseModel):
475
+ name: Annotated[str, Field(title="Name")]
476
+
477
+
478
+ class OrgRead(BaseModel):
479
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
480
+ id: Annotated[UUID, Field(title="Id")]
481
+ name: Annotated[str, Field(title="Name")]
482
+ slug: Annotated[str, Field(title="Slug")]
483
+
484
+
485
+ class ProjectCreate(BaseModel):
486
+ description: Annotated[str | None, Field(title="Description")] = None
487
+ name: Annotated[str, Field(title="Name")]
488
+ project_id: Annotated[str | None, Field(title="Project Id")] = None
489
+
490
+
491
+ class ProjectRead(BaseModel):
492
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
493
+ description: Annotated[str | None, Field(title="Description")] = None
494
+ id: Annotated[UUID, Field(title="Id")]
495
+ name: Annotated[str, Field(title="Name")]
496
+ updated_at: Annotated[AwareDatetime, Field(title="Updated At")]
497
+
498
+
499
+ class RefreshRequest(BaseModel):
500
+ refresh_token: Annotated[str, Field(title="Refresh Token")]
501
+
502
+
503
+ class RegisterRequest(LoginRequest):
504
+ pass
505
+
506
+
507
+ class ResetPasswordRequest(BaseModel):
508
+ new_password: Annotated[str, Field(title="New Password")]
509
+ token: Annotated[str, Field(title="Token")]
510
+
511
+
512
+ class RestartBody(BaseModel):
513
+ service: Annotated[str | None, Field(title="Service")] = None
514
+
515
+
516
+ class Challenge(StrEnum):
517
+ dns_01 = "dns-01"
518
+ http_01 = "http-01"
519
+
520
+
521
+ class Provider(StrEnum):
522
+ letsencrypt = "letsencrypt"
523
+ custom = "custom"
524
+ none = "none"
525
+
526
+
527
+ class SSLConfig(BaseModel):
528
+ model_config = ConfigDict(
529
+ extra="forbid",
530
+ )
531
+ certPath: Annotated[str | None, Field(title="Certpath")] = None
532
+ challenge: Annotated[Challenge | None, Field(title="Challenge")] = "dns-01"
533
+ dnsProvider: Annotated[str | None, Field(title="Dnsprovider")] = None
534
+ email: Annotated[str | None, Field(title="Email")] = None
535
+ enabled: Annotated[bool | None, Field(title="Enabled")] = True
536
+ keyPath: Annotated[str | None, Field(title="Keypath")] = None
537
+ provider: Annotated[Provider | None, Field(title="Provider")] = "letsencrypt"
538
+
539
+
540
+ class ServerCreate(BaseModel):
541
+ flavour: Annotated[str | None, Field(title="Flavour")] = None
542
+ image: Annotated[str | None, Field(title="Image")] = None
543
+ location: Annotated[str | None, Field(title="Location")] = None
544
+ name: Annotated[str, Field(title="Name")]
545
+ provider: Annotated[str | None, Field(title="Provider")] = "hetzner"
546
+ region: Annotated[str | None, Field(title="Region")] = None
547
+ server_type: Annotated[str | None, Field(title="Server Type")] = None
548
+
549
+
550
+ class ServerDnsRead(BaseModel):
551
+ provider: Annotated[str, Field(title="Provider")]
552
+ records: Annotated[Any | None, Field(title="Records")] = None
553
+ server_id: Annotated[UUID, Field(title="Server Id")]
554
+ verified: Annotated[bool | None, Field(title="Verified")] = False
555
+
556
+
557
+ class ServerImport(BaseModel):
558
+ host: Annotated[str, Field(title="Host")]
559
+ name: Annotated[str, Field(title="Name")]
560
+ user: Annotated[str | None, Field(title="User")] = "root"
561
+
562
+
563
+ class ServerServiceRead(BaseModel):
564
+ active_slot: Annotated[str | None, Field(title="Active Slot")] = None
565
+ id: Annotated[UUID, Field(title="Id")]
566
+ image: Annotated[str | None, Field(title="Image")] = None
567
+ server_id: Annotated[UUID, Field(title="Server Id")]
568
+ service_id: Annotated[str, Field(title="Service Id")]
569
+ service_type: Annotated[str, Field(title="Service Type")]
570
+ status: Annotated[str | None, Field(title="Status")] = None
571
+
572
+
573
+ class ServerSslRead(BaseModel):
574
+ challenge: Annotated[str | None, Field(title="Challenge")] = None
575
+ domain: Annotated[str | None, Field(title="Domain")] = None
576
+ provider: Annotated[str, Field(title="Provider")]
577
+ provisioned: Annotated[bool | None, Field(title="Provisioned")] = False
578
+ server_id: Annotated[UUID, Field(title="Server Id")]
579
+
580
+
581
+ class ServerStatusResponse(BaseModel):
582
+ """
583
+ Full server status from SSH inspection.
584
+ """
585
+
586
+ containers: Annotated[
587
+ list[ContainerStatus] | None, Field(title="Containers", validate_default=True)
588
+ ] = []
589
+ health: Annotated[dict[str, str] | None, Field(title="Health")] = {}
590
+ server_ip: Annotated[str, Field(title="Server Ip")]
591
+ server_name: Annotated[str, Field(title="Server Name")]
592
+ system: Annotated[str | None, Field(title="System")] = None
593
+
594
+
595
+ class ServerUsageDetail(BaseModel):
596
+ cost: Annotated[float | None, Field(title="Cost")] = 0.0
597
+ destroyed_at: Annotated[AwareDatetime | None, Field(title="Destroyed At")] = None
598
+ hours: Annotated[float, Field(title="Hours")]
599
+ provider: Annotated[str, Field(title="Provider")]
600
+ provisioned_at: Annotated[AwareDatetime, Field(title="Provisioned At")]
601
+ server_id: Annotated[str, Field(title="Server Id")]
602
+ server_name: Annotated[str, Field(title="Server Name")]
603
+ server_type: Annotated[str | None, Field(title="Server Type")] = None
604
+
605
+
606
+ class LogFormat(StrEnum):
607
+ """
608
+ Log format this service writes to stdout. 'json' enables structured field extraction in Loki (level, request_id). 'auto' (default when omitted) infers from the image name.
609
+ """
610
+
611
+ json = "json"
612
+ plain = "plain"
613
+ auto = "auto"
614
+
615
+
616
+ class Type(StrEnum):
617
+ compute = "compute"
618
+ persistence = "persistence"
619
+ observability = "observability"
620
+
621
+
622
+ class ServiceDef(BaseModel):
623
+ """
624
+ A single service entry under ``cloud.services[]``.
625
+
626
+ The ``type`` field selects required-field rules:
627
+
628
+ - ``compute`` → requires ``port`` and ``healthPath``
629
+ - ``persistence`` → requires ``port``
630
+ - ``observability``→ image only
631
+ """
632
+
633
+ model_config = ConfigDict(
634
+ extra="forbid",
635
+ )
636
+ backupPolicy: BackupPolicyDef | None = None
637
+ build: Annotated[dict[str, Any] | None, Field(title="Build")] = None
638
+ command: Annotated[str | None, Field(title="Command")] = None
639
+ dependsOn: Annotated[list[str] | None, Field(title="Dependson")] = None
640
+ environment: Annotated[dict[str, str] | None, Field(title="Environment")] = None
641
+ environments: Annotated[list[str] | None, Field(title="Environments")] = None
642
+ extraPorts: Annotated[list[str] | None, Field(title="Extraports")] = None
643
+ """
644
+ Additional host:container port mappings passed through verbatim to docker-compose. Useful for services with multiple exposed ports (e.g. MailHog: 1025 SMTP + 8025 web UI).
645
+ """
646
+ healthPath: Annotated[str | None, Field(title="Healthpath")] = None
647
+ healthcheck: Annotated[dict[str, Any] | None, Field(title="Healthcheck")] = None
648
+ hostPort: Annotated[int | None, Field(title="Hostport")] = None
649
+ id: Annotated[str, Field(min_length=1, title="Id")]
650
+ image: Annotated[str, Field(min_length=1, title="Image")]
651
+ labels: Annotated[dict[str, str] | None, Field(title="Labels")] = None
652
+ lifecycle: LifecycleDef | None = None
653
+ logFormat: Annotated[LogFormat | None, Field(title="Logformat")] = None
654
+ """
655
+ Log format this service writes to stdout. 'json' enables structured field extraction in Loki (level, request_id). 'auto' (default when omitted) infers from the image name.
656
+ """
657
+ port: Annotated[int | None, Field(title="Port")] = None
658
+ resources: Annotated[dict[str, Any] | None, Field(title="Resources")] = None
659
+ routePrefix: Annotated[str | None, Field(title="Routeprefix")] = None
660
+ server: Annotated[str | None, Field(title="Server")] = None
661
+ """
662
+ Server role for multi-server deployments. Services with the same server value are co-located on one VPS. If omitted, all services go to the default (primary) server.
663
+ """
664
+ type: Annotated[Type, Field(title="Type")]
665
+ volumes: Annotated[list[str] | None, Field(title="Volumes")] = None
666
+
667
+
668
+ class SingleContainerSpec(BaseModel):
669
+ model_config = ConfigDict(
670
+ extra="forbid",
671
+ )
672
+ healthPath: Annotated[str, Field(min_length=1, title="Healthpath")]
673
+ image: Annotated[str, Field(min_length=1, title="Image")]
674
+ port: Annotated[int, Field(gt=0, title="Port")]
675
+
676
+
677
+ class SnapshotResponse(BaseModel):
678
+ id: Annotated[int, Field(title="Id")]
679
+ received_at: Annotated[str, Field(title="Received At")]
680
+ snapshot: ArchitectureSnapshot
681
+
682
+
683
+ class SnapshotSummary(BaseModel):
684
+ id: Annotated[int, Field(title="Id")]
685
+ name: Annotated[str, Field(title="Name")]
686
+ received_at: Annotated[str, Field(title="Received At")]
687
+ system_count: Annotated[int, Field(title="System Count")]
688
+
689
+
690
+ class StaticBuild(BaseModel):
691
+ model_config = ConfigDict(
692
+ extra="forbid",
693
+ )
694
+ command: Annotated[str, Field(min_length=1, title="Command")]
695
+ installCommand: Annotated[str | None, Field(title="Installcommand")] = None
696
+ outputDir: Annotated[str, Field(min_length=1, title="Outputdir")]
697
+
698
+
699
+ class Router(StrEnum):
700
+ spa = "spa"
701
+ history = "history"
702
+ hash = "hash"
703
+
704
+
705
+ class StaticServe(BaseModel):
706
+ model_config = ConfigDict(
707
+ extra="forbid",
708
+ )
709
+ router: Annotated[Router, Field(title="Router")]
710
+
711
+
712
+ class StatusResponse(BaseModel):
713
+ """
714
+ Generic status response for operations that don't return data.
715
+ """
716
+
717
+ status: Annotated[str, Field(title="Status")]
718
+
719
+
720
+ class SwitchBody(BaseModel):
721
+ component: Annotated[str, Field(title="Component")]
722
+ to_color: Annotated[str, Field(title="To Color")]
723
+
724
+
725
+ class TokenResponse(BaseModel):
726
+ access_token: Annotated[str, Field(title="Access Token")]
727
+ expires_in: Annotated[int | None, Field(title="Expires In")] = None
728
+ refresh_token: Annotated[str | None, Field(title="Refresh Token")] = None
729
+ token_type: Annotated[str | None, Field(title="Token Type")] = "bearer"
730
+
731
+
732
+ class UpdateBody(BaseModel):
733
+ component: Annotated[str, Field(title="Component")]
734
+ new_image: Annotated[str, Field(title="New Image")]
735
+
736
+
737
+ class UsageEstimate(BaseModel):
738
+ activeServers: Annotated[int, Field(title="Activeservers")]
739
+ currentMonthCost: Annotated[float | None, Field(title="Currentmonthcost")] = 0.0
740
+ currentMonthHours: Annotated[float, Field(title="Currentmonthhours")]
741
+ org_id: Annotated[str, Field(title="Org Id")]
742
+ projectedMonthCost: Annotated[float | None, Field(title="Projectedmonthcost")] = 0.0
743
+ projectedMonthHours: Annotated[float, Field(title="Projectedmonthhours")]
744
+
745
+
746
+ class UsageHistoryItem(BaseModel):
747
+ cost: Annotated[float | None, Field(title="Cost")] = 0.0
748
+ period_end: Annotated[AwareDatetime, Field(title="Period End")]
749
+ period_start: Annotated[AwareDatetime, Field(title="Period Start")]
750
+ serverCount: Annotated[int, Field(title="Servercount")]
751
+ vpsHours: Annotated[float, Field(title="Vpshours")]
752
+
753
+
754
+ class UsageSummary(BaseModel):
755
+ activeServerCount: Annotated[int, Field(title="Activeservercount")]
756
+ org_id: Annotated[str, Field(title="Org Id")]
757
+ period_end: Annotated[AwareDatetime, Field(title="Period End")]
758
+ period_start: Annotated[AwareDatetime, Field(title="Period Start")]
759
+ servers: Annotated[
760
+ list[ServerUsageDetail] | None, Field(title="Servers", validate_default=True)
761
+ ] = []
762
+ totalCost: Annotated[float | None, Field(title="Totalcost")] = 0.0
763
+ totalVpsHours: Annotated[float, Field(title="Totalvpshours")]
764
+
765
+
766
+ class UserRead(BaseModel):
767
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
768
+ email: Annotated[str, Field(title="Email")]
769
+ email_verified: Annotated[bool | None, Field(title="Email Verified")] = False
770
+ id: Annotated[UUID, Field(title="Id")]
771
+ is_active: Annotated[bool, Field(title="Is Active")]
772
+
773
+
774
+ class ValidateBody(BaseModel):
775
+ manifest_path: Annotated[str | None, Field(title="Manifest Path")] = None
776
+
777
+
778
+ class ValidateResponse(BaseModel):
779
+ """
780
+ Response for manifest validation.
781
+ """
782
+
783
+ path: Annotated[str, Field(title="Path")]
784
+ valid: Annotated[bool, Field(title="Valid")]
785
+
786
+
787
+ class ValidationError(BaseModel):
788
+ ctx: Annotated[dict[str, Any] | None, Field(title="Context")] = None
789
+ input: Annotated[Any | None, Field(title="Input")] = None
790
+ loc: Annotated[list[str | int], Field(title="Location")]
791
+ msg: Annotated[str, Field(title="Message")]
792
+ type: Annotated[str, Field(title="Error Type")]
793
+
794
+
795
+ class VaultGetResponse(BaseModel):
796
+ """
797
+ Response for vault get operations.
798
+ """
799
+
800
+ key: Annotated[str, Field(title="Key")]
801
+ value: Annotated[str, Field(title="Value")]
802
+
803
+
804
+ class VaultSetRequest(BaseModel):
805
+ environment_id: Annotated[str | None, Field(title="Environment Id")] = None
806
+ key: Annotated[str, Field(title="Key")]
807
+ value: Annotated[str, Field(title="Value")]
808
+
809
+
810
+ class VaultSetResponse(BaseModel):
811
+ """
812
+ Response for vault set operations.
813
+ """
814
+
815
+ key: Annotated[str, Field(title="Key")]
816
+ status: Annotated[str, Field(title="Status")]
817
+
818
+
819
+ class VerifyEmailRequest(BaseModel):
820
+ token: Annotated[str, Field(title="Token")]
821
+
822
+
823
+ class WorkspaceRead(BaseModel):
824
+ apiEndpoint: Annotated[str | None, Field(title="Apiendpoint")] = None
825
+ currentEnvironment: Annotated[str | None, Field(title="Currentenvironment")] = None
826
+ currentServer: Annotated[str | None, Field(title="Currentserver")] = None
827
+ hasAccountKey: Annotated[bool | None, Field(title="Hasaccountkey")] = False
828
+ projectKeyCount: Annotated[int | None, Field(title="Projectkeycount")] = 0
829
+ version: Annotated[int, Field(title="Version")]
830
+
831
+
832
+ class WorkspaceUpdate(BaseModel):
833
+ current_environment: Annotated[str | None, Field(title="Current Environment")] = (
834
+ None
835
+ )
836
+ current_server: Annotated[str | None, Field(title="Current Server")] = None
837
+
838
+
839
+ class CapacityReport(BaseModel):
840
+ limits_per_org: CapacityOrgLimits
841
+ region: Annotated[str, Field(title="Region")]
842
+ servers: CapacityServers
843
+
844
+
845
+ class GatewayDef(BaseModel):
846
+ model_config = ConfigDict(
847
+ extra="forbid",
848
+ )
849
+ domains: Annotated[list[GatewayDomain], Field(min_length=1, title="Domains")]
850
+ ssl: SSLConfig | None = None
851
+
852
+
853
+ class HTTPValidationError(BaseModel):
854
+ detail: Annotated[list[ValidationError] | None, Field(title="Detail")] = None
855
+
856
+
857
+ class HealthRead(BaseModel):
858
+ capacity: CapacityReport | None = None
859
+ instance: Annotated[str, Field(title="Instance")]
860
+ region: Annotated[str, Field(title="Region")]
861
+ region_url: Annotated[str, Field(title="Region Url")]
862
+ status: Annotated[str, Field(title="Status")]
863
+ version: Annotated[str, Field(title="Version")]
864
+
865
+
866
+ class LoggingDef(BaseModel):
867
+ """
868
+ Logging subsystem — Promtail ships container stdout to Loki.
869
+ """
870
+
871
+ model_config = ConfigDict(
872
+ extra="forbid",
873
+ )
874
+ enabled: Annotated[bool | None, Field(title="Enabled")] = True
875
+ loki: LokiDef | None = None
876
+
877
+
878
+ class MeResponse(BaseModel):
879
+ orgs: Annotated[list[OrgBrief], Field(title="Orgs")]
880
+ user: UserRead
881
+
882
+
883
+ class NativeBuild(BaseModel):
884
+ model_config = ConfigDict(
885
+ extra="forbid",
886
+ )
887
+ apiVersion: Annotated[Literal["forktex.cloud/v1beta2"], Field(title="Apiversion")]
888
+ build: NativeBuildSpec
889
+ infrastructure: InfrastructureDef | None = None
890
+ kind: Annotated[Literal["NativeBuild"], Field(title="Kind")]
891
+ metadata: MetadataDef
892
+
893
+
894
+ class ObservabilityDefInput(BaseModel):
895
+ """
896
+ Opt-in monitoring stack deployed alongside the application on the VPS.
897
+
898
+ When enabled, Loki + Promtail aggregate container logs (queryable via
899
+ ``forktex cloud logs --query``), and Prometheus scrapes metrics from
900
+ services that expose ``/metrics``.
901
+ """
902
+
903
+ model_config = ConfigDict(
904
+ extra="forbid",
905
+ )
906
+ enabled: Annotated[bool | None, Field(title="Enabled")] = False
907
+ logging: LoggingDef | None = None
908
+ metrics: MetricsDef | None = None
909
+
910
+
911
+ class ObservabilityDefOutput(ObservabilityDefInput):
912
+ """
913
+ Opt-in monitoring stack deployed alongside the application on the VPS.
914
+
915
+ When enabled, Loki + Promtail aggregate container logs (queryable via
916
+ ``forktex cloud logs --query``), and Prometheus scrapes metrics from
917
+ services that expose ``/metrics``.
918
+ """
919
+
920
+
921
+ class ProjectDeploymentInput(BaseModel):
922
+ """
923
+ Multi-service single-server deployment to a single Hetzner VPS.
924
+ """
925
+
926
+ model_config = ConfigDict(
927
+ extra="forbid",
928
+ )
929
+ apiVersion: Annotated[Literal["forktex.cloud/v1beta2"], Field(title="Apiversion")]
930
+ deployment: DeploymentDef | None = None
931
+ gateway: GatewayDef | None = None
932
+ infrastructure: InfrastructureDef
933
+ kind: Annotated[Literal["ProjectDeployment"], Field(title="Kind")]
934
+ metadata: MetadataDef
935
+ observability: ObservabilityDefInput | None = None
936
+ services: Annotated[list[ServiceDef], Field(min_length=1, title="Services")]
937
+
938
+
939
+ class ProjectDeploymentOutput(BaseModel):
940
+ """
941
+ Multi-service single-server deployment to a single Hetzner VPS.
942
+ """
943
+
944
+ model_config = ConfigDict(
945
+ extra="forbid",
946
+ )
947
+ apiVersion: Annotated[Literal["forktex.cloud/v1beta2"], Field(title="Apiversion")]
948
+ deployment: DeploymentDef | None = None
949
+ gateway: GatewayDef | None = None
950
+ infrastructure: InfrastructureDef
951
+ kind: Annotated[Literal["ProjectDeployment"], Field(title="Kind")]
952
+ metadata: MetadataDef
953
+ observability: ObservabilityDefOutput | None = None
954
+ services: Annotated[list[ServiceDef], Field(min_length=1, title="Services")]
955
+
956
+
957
+ class ServerRead(BaseModel):
958
+ created_at: Annotated[AwareDatetime, Field(title="Created At")]
959
+ dns: ServerDnsRead | None = None
960
+ id: Annotated[UUID, Field(title="Id")]
961
+ image: Annotated[str | None, Field(title="Image")] = None
962
+ ipv4: Annotated[str | None, Field(title="Ipv4")] = None
963
+ location: Annotated[str | None, Field(title="Location")] = None
964
+ name: Annotated[str, Field(title="Name")]
965
+ provider: Annotated[str, Field(title="Provider")]
966
+ provider_id: Annotated[str | None, Field(title="Provider Id")] = None
967
+ server_type: Annotated[str | None, Field(title="Server Type")] = None
968
+ services: Annotated[list[ServerServiceRead] | None, Field(title="Services")] = None
969
+ ssh_key_id: Annotated[str | None, Field(title="Ssh Key Id")] = None
970
+ ssl: ServerSslRead | None = None
971
+ status: Annotated[str, Field(title="Status")]
972
+
973
+
974
+ class SingleContainerInput(BaseModel):
975
+ model_config = ConfigDict(
976
+ extra="forbid",
977
+ )
978
+ apiVersion: Annotated[Literal["forktex.cloud/v1beta2"], Field(title="Apiversion")]
979
+ container: SingleContainerSpec
980
+ gateway: GatewayDef | None = None
981
+ infrastructure: InfrastructureDef | None = None
982
+ kind: Annotated[Literal["SingleContainer"], Field(title="Kind")]
983
+ metadata: MetadataDef
984
+
985
+
986
+ class SingleContainerOutput(SingleContainerInput):
987
+ pass
988
+
989
+
990
+ class StaticSiteInput(BaseModel):
991
+ model_config = ConfigDict(
992
+ extra="forbid",
993
+ )
994
+ apiVersion: Annotated[Literal["forktex.cloud/v1beta2"], Field(title="Apiversion")]
995
+ build: StaticBuild
996
+ gateway: GatewayDef | None = None
997
+ kind: Annotated[Literal["StaticSite"], Field(title="Kind")]
998
+ metadata: MetadataDef
999
+ serve: StaticServe
1000
+
1001
+
1002
+ class StaticSiteOutput(StaticSiteInput):
1003
+ pass
1004
+
1005
+
1006
+ class UpRequest(BaseModel):
1007
+ assets_tarball_b64: Annotated[str | None, Field(title="Assets Tarball B64")] = None
1008
+ env: Annotated[str | None, Field(title="Env")] = None
1009
+ flavour: Annotated[str | None, Field(title="Flavour")] = None
1010
+ manifest_data: Annotated[
1011
+ ProjectDeploymentInput
1012
+ | StaticSiteInput
1013
+ | SingleContainerInput
1014
+ | NativeBuild
1015
+ | None,
1016
+ Field(title="Manifest Data"),
1017
+ ] = None
1018
+ name: Annotated[str | None, Field(title="Name")] = None
1019
+ region: Annotated[str | None, Field(title="Region")] = None
1020
+ skip_dns: Annotated[bool | None, Field(title="Skip Dns")] = False
1021
+ skip_ssl: Annotated[bool | None, Field(title="Skip Ssl")] = False
1022
+
1023
+
1024
+ class UsageResponse(BaseModel):
1025
+ current: UsageSummary
1026
+ history: Annotated[
1027
+ list[UsageHistoryItem] | None, Field(title="History", validate_default=True)
1028
+ ] = []
1029
+
1030
+
1031
+ class DeployRequest(BaseModel):
1032
+ assets_tarball_b64: Annotated[str | None, Field(title="Assets Tarball B64")] = None
1033
+ environment_id: Annotated[str | None, Field(title="Environment Id")] = None
1034
+ manifest_data: Annotated[
1035
+ ProjectDeploymentInput
1036
+ | StaticSiteInput
1037
+ | SingleContainerInput
1038
+ | NativeBuild
1039
+ | None,
1040
+ Field(title="Manifest Data"),
1041
+ ] = None
1042
+ server_id: Annotated[str, Field(title="Server Id")]
1043
+ service: Annotated[str | None, Field(title="Service")] = None
1044
+ tags: Annotated[list[str] | None, Field(title="Tags")] = None
1045
+
1046
+
1047
+ class DownRequest(BaseModel):
1048
+ keep_dns: Annotated[bool | None, Field(title="Keep Dns")] = False
1049
+ manifest_data: Annotated[
1050
+ ProjectDeploymentInput
1051
+ | StaticSiteInput
1052
+ | SingleContainerInput
1053
+ | NativeBuild
1054
+ | None,
1055
+ Field(title="Manifest Data"),
1056
+ ] = None
1057
+ name: Annotated[str | None, Field(title="Name")] = None